home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / share / nikto / plugins / LW.pm next >
Text File  |  2005-10-19  |  145KB  |  10,308 lines

  1. # libwhisker v1.7
  2. # libwhisker is a collection of routines used by whisker
  3.  
  4. #
  5.  
  6. # libwhisker copyright 2000,2001,2002 rfp.labs
  7.  
  8. #
  9.  
  10. # This program is free software; you can redistribute it and/or
  11.  
  12. # modify it under the terms of the GNU General Public License
  13.  
  14. # as published by the Free Software Foundation; either version 2
  15.  
  16. # of the License, or (at your option) any later version.
  17.  
  18. #
  19.  
  20. # This program is distributed in the hope that it will be useful,
  21.  
  22. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  23.  
  24. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25.  
  26. # GNU General Public License for more details.
  27.  
  28.  
  29. #
  30.  
  31. # More information can be found at http://www.wiretrip.net/rfp/
  32.  
  33. # Libwhisker mailing list and resources are also available at
  34.  
  35. # http://sourceforge.net/projects/whisker/
  36.  
  37. #
  38.  
  39.  
  40.  
  41. package LW;
  42.  
  43. use 5.004;
  44.  
  45. $LW::VERSION="1.7";
  46.  
  47.  
  48. ####### external module tests ###################################
  49.  
  50.  
  51.  
  52. BEGIN {
  53.  
  54.  
  55.  
  56. ## LW module manager stuff ##
  57.  
  58.  
  59.  
  60.     %LW::available        = ();
  61.  
  62.     $LW::LW_HAS_SOCKET    = 0;
  63.  
  64.     $LW::LW_HAS_SSL        = 0;
  65.  
  66.     $LW::LW_SSL_LIB        = 0;
  67.  
  68.     $LW::LW_NONBLOCK_CONNECT= 0;
  69.  
  70.  
  71.  
  72. ## binary helper - may contain functions substituted further down ##
  73.  
  74.         eval "use LW::bin"; # do we have libwhisker binary helpers?
  75.  
  76.         if($@){ $LW::available{'LW::bin'}=$LW::bin::VERSION; }
  77.  
  78.  
  79.  
  80. ## encode subpackage ##
  81.  
  82.     eval "require MIME::Base64";
  83.  
  84.     if($@){
  85.  
  86.             *encode_base64 = \&encode_base64_perl; 
  87.  
  88.             *decode_base64 = \&decode_base64_perl; 
  89.  
  90.     } else{ 
  91.  
  92.         # MIME::Base64 typically has faster C versions
  93.  
  94.         $LW::available{'mime::base64'}=$MIME::Base64::VERSION;
  95.  
  96.             *encode_base64 = \&MIME::Base64::encode_base64;
  97.  
  98.             *decode_base64 = \&MIME::Base64::decode_base64;}
  99.  
  100.  
  101.  
  102. ## md5 subpackage ##
  103.  
  104.     eval "require MD5";
  105.  
  106.     if(!$@){ $LW::available{'md5'}=$MD5::VERSION;}
  107.  
  108.  
  109.  
  110. ## http subpackage ##
  111.  
  112.         eval "use Socket"; # do we have socket support?
  113.  
  114.         if($@){ $LW::LW_HAS_SOCKET=0; }
  115.  
  116.         else { $LW::LW_HAS_SOCKET=1;
  117.  
  118.                 $LW::available{'socket'}=$Socket::VERSION;}
  119.  
  120.  
  121.  
  122.     if($LW_HAS_SOCKET){
  123.  
  124.     eval "use Net::SSLeay"; # do we have SSL support?
  125.  
  126.         if($@){ $LW::LW_HAS_SSL=0; }
  127.  
  128.         else { $LW::LW_HAS_SSL=1;
  129.  
  130.                 $LW::LW_SSL_LIB=1;
  131.  
  132.                 $LW::available{'net::ssleay'}=$Net::SSLeay::VERSION;
  133.  
  134.                 Net::SSLeay::load_error_strings();
  135.  
  136.                 Net::SSLeay::SSLeay_add_ssl_algorithms();
  137.  
  138.                 Net::SSLeay::randomize();}
  139.  
  140.         if(!$LW::LW_HAS_SSL){
  141.  
  142.                 eval "use Net::SSL"; # different SSL lib
  143.  
  144.                 if($@){ $LW::LW_HAS_SSL=0; }
  145.  
  146.                 else { $LW::LW_HAS_SSL=1;
  147.  
  148.                         $LW::LW_SSL_LIB=2;
  149.  
  150.                         $LW::available{'net::ssl'}=$Net::SSL::VERSION;}
  151.  
  152.         }
  153.  
  154.  
  155.  
  156. ## non-blocking IO ##
  157.  
  158.  
  159.  
  160.     if($^O!~/Win32/){
  161.  
  162.         eval "use POSIX qw(:errno_h :fcntl_h)"; # better
  163.  
  164.         if(!$@){
  165.  
  166.             $LW::LW_NONBLOCK_CONNECT=1;
  167.  
  168.         }
  169.  
  170.     }
  171.  
  172.  
  173.  
  174.     } # if($LW_HAS_SOCKET)
  175.  
  176.  
  177.  
  178. } # BEGIN
  179.  
  180.  
  181.  
  182. ####### package variables #######################################
  183.  
  184.  
  185.  
  186. ## crawl subpackage ##
  187.  
  188.     %LW::crawl_config=(    'save_cookies'    => 0,
  189.  
  190.                 'reuse_cookies'    => 1,
  191.  
  192.                 'save_offsites'    => 0,
  193.  
  194.                 'follow_moves'    => 1,
  195.  
  196.                 'url_limit'    => 1000,
  197.  
  198.                 'use_params'    => 0,
  199.  
  200.                 'params_double_record' => 0,
  201.  
  202.                 'skip_ext'    => '.gif .jpg .gz .mp3 .swf .zip ',
  203.  
  204.                 'save_skipped'    => 0,
  205.  
  206.                 'save_referrers'=> 0,
  207.  
  208.                 'do_head'    => 0,
  209.  
  210.                 'callback'    => 0,
  211.  
  212.                 'slashdot_bug'    => 1,
  213.  
  214.                 'normalize_uri'    => 1,
  215.  
  216.                 'source_callback' => 0
  217.  
  218.             );
  219.  
  220.  
  221.  
  222.  
  223.  
  224.     @LW::crawl_urls=();;
  225.  
  226.     %LW::crawl_server_tags=();
  227.  
  228.     %LW::crawl_referrers=();
  229.  
  230.     %LW::crawl_offsites=();
  231.  
  232.     %LW::crawl_cookies=();
  233.  
  234.     %LW::crawl_forms=();
  235.  
  236.     %LW::crawl_temp=();
  237.  
  238.  
  239.  
  240.     # this idea/structure was taken from HTML::LinkExtor.pm,
  241.  
  242.     # copyright 2000 Gisle Aas and Michael A. Chase
  243.  
  244.     %LW::crawl_linktags = (
  245.  
  246.          'a'       => 'href',
  247.  
  248.          'applet'  => [qw(codebase archive code)],
  249.  
  250.          'area'    => 'href',
  251.  
  252.          'base'    => 'href',
  253.  
  254.          'bgsound' => 'src',
  255.  
  256.          'blockquote' => 'cite',
  257.  
  258.          'body'    => 'background',
  259.  
  260.          'del'     => 'cite',
  261.  
  262.          'embed'   => [qw(src pluginspage)],
  263.  
  264.          'form'    => 'action',
  265.  
  266.          'frame'   => [qw(src longdesc)],
  267.  
  268.          'iframe'  => [qw(src longdesc)],
  269.  
  270.          'ilayer'  => 'background',
  271.  
  272.          'img'     => [qw(src lowsrc longdesc usemap)],
  273.  
  274.          'input'   => [qw(src usemap)],
  275.  
  276.          'ins'     => 'cite',
  277.  
  278.          'isindex' => 'action',
  279.  
  280.          'head'    => 'profile',
  281.  
  282.          'layer'   => [qw(background src)],
  283.  
  284.          'link'    => 'href',
  285.  
  286.          'object'  => [qw(codebase data archive usemap)],
  287.  
  288.          'q'       => 'cite',
  289.  
  290.          'script'  => 'src',
  291.  
  292.          'table'   => 'background',
  293.  
  294.          'td'      => 'background',
  295.  
  296.          'th'      => 'background',
  297.  
  298.          'xmp'     => 'href',
  299.  
  300.     );
  301.  
  302.  
  303.  
  304.  
  305.  
  306. ## forms subpackage ##
  307.  
  308.     @LW::forms_found=();
  309.  
  310.     %LW::forms_current=();
  311.  
  312.  
  313.  
  314.  
  315.  
  316. ## http subpackage ##
  317.  
  318.     my $SOCKSTATE=0;
  319.  
  320.     my $TIMEOUT=10; # default
  321.  
  322.     my ($STATS_REQS,$STATS_SYNS)=(0,0);
  323.  
  324.     my ($LAST_HOST,$LAST_INET_ATON,$LAST_SSL)=('','',0);
  325.  
  326.     my ($OUTGOING_QUEUE,$INCOMING_QUEUE)=('','');
  327.  
  328.     my ($SSL_CTX, $SSL_THINGY);
  329.  
  330.  
  331.  
  332.     my %http_host_cache=();
  333.  
  334.     # order is following:
  335.  
  336.     # [0] - SOCKET
  337.  
  338.     # [1] - $SOCKSTATE
  339.  
  340.     # [2] - INET_ATON
  341.  
  342.     # [3] - $SSL_CTX
  343.  
  344.     # [4] - $SSL_THINGY
  345.  
  346.     # [5] - $OUTGOING_QUEUE
  347.  
  348.     # [6] - $INCOMING_QUEUE
  349.  
  350.     # [7] - $STATS_SYNS
  351.  
  352.     # [8] - $STATS_REQS
  353.  
  354.  
  355.  
  356.     my $Z; # array ref to current host specs
  357.  
  358.  
  359.  
  360. =pod
  361.  
  362.  
  363.  
  364.  
  365.  
  366. =head1 ++ Sub package: anti-ids
  367.  
  368.  
  369.  
  370. The anti-ids sub package implements management routines for various
  371.  
  372. rewriting/encoding in order to evade intrusion detection systems.
  373.  
  374.  
  375.  
  376. =cut
  377.  
  378.  
  379.  
  380. ########################################################################
  381.  
  382.  
  383.  
  384. =pod
  385.  
  386.  
  387.  
  388. =head1 - Function: LW::anti_ids
  389.  
  390.  
  391.  
  392. Params: \%hin, $modes
  393.  
  394. Return: nothing
  395.  
  396.  
  397.  
  398. LW::anti_ids computes the proper anti-ids encoding/tricks specified by
  399.  
  400. $modes, and sets up %hin in order to use those tricks.  Valid modes
  401.  
  402. are (the mode numbers are the same as those found in whisker 1.4):
  403.  
  404.  
  405.  
  406. 1 -    Encode some of the characters via normal URL encoding
  407.  
  408. 2 -    Insert directory self-references (/./)
  409.  
  410. 3 -    Premature URL ending (make it appear the request line is done)
  411.  
  412. 4 -    Prepend a long random string in the form of "/string/../URL"
  413.  
  414. 5 -    Add a fake URL parameter
  415.  
  416. 6 -    Use a tab instead of a space as a request spacer
  417.  
  418. 7 -    Change the case of the URL around (works against Windows and Novell)
  419.  
  420. 8 -    Change normal seperators ('/') to Windows version ('\')
  421.  
  422. 9 -    Session splicing (sending data in multiple packets)
  423.  
  424.  
  425.  
  426. You can set multiple modes by setting the string to contain all the modes
  427.  
  428. desired; i.e. $modes="146" will use modes 1, 4, and 6.
  429.  
  430.  
  431.  
  432. =cut
  433.  
  434.  
  435.  
  436.  
  437.  
  438. sub anti_ids {
  439.  
  440.     my ($rhin,$modes)=(shift,shift);
  441.  
  442.     my (@T,$x,$c,$s,$y);
  443.  
  444.     my $ENCODED=0;
  445.  
  446.     my $W = $$rhin{'whisker'};
  447.  
  448.  
  449.  
  450.     return if(!(defined $rhin && ref($rhin)));
  451.  
  452.  
  453.  
  454.     # in case they didn't do it already
  455.  
  456.     $$rhin{'whisker'}->{'uri_orig'}=$$rhin{'whisker'}->{'uri'};
  457.  
  458.  
  459.  
  460.     # note: order is important!
  461.  
  462.  
  463.  
  464.     # mode 9 - session splicing
  465.  
  466.     if($modes=~/9/){
  467.  
  468.         $$rhin{'whisker'}->{'ids_session_splice'}=1;
  469.  
  470.     }
  471.  
  472.  
  473.  
  474.     # mode 4 - prepend long random string
  475.  
  476.     if($modes=~/4/){$s='';
  477.  
  478.         if($$W{'uri'}=~m#^/#){
  479.  
  480.             $y=&utils_randstr;
  481.  
  482.             $s.=$y while(length($s)<512);
  483.  
  484.             $$W{'uri'}="/$s/..".$$W{'uri'};
  485.  
  486.         }
  487.  
  488.     }
  489.  
  490.  
  491.  
  492.     # mode 7  - (windows) random case sensitivity
  493.  
  494.     if($modes=~/7/){ 
  495.  
  496.         @T=split(//,$$W{'uri'});
  497.  
  498.         for($x=0;$x<(scalar @T);$x++){
  499.  
  500.             if((rand()*2)%2 == 1){
  501.  
  502.                 $T[$x]=uc($T[$x]);}}
  503.  
  504.         $$W{'uri'}=join('',@T);
  505.  
  506.     }
  507.  
  508.  
  509.  
  510.     # mode 2 - directory self-reference (/./)
  511.  
  512.     if($modes=~/2/){
  513.  
  514.         $$W{'uri'}=~s#/#/./#g;
  515.  
  516.     }
  517.  
  518.  
  519.  
  520.  
  521.  
  522.     # mode 8 - windows directory separator (\)
  523.  
  524.     if($modes=~/8/){
  525.  
  526.         $$W{'uri'}=~s#/#\\#g;
  527.  
  528.         $$W{'uri'}=~s#^\\#/#;
  529.  
  530.         $$W{'uri'}=~s#^(http|file|ftp|nntp|news|telnet):\\#$1://#;
  531.  
  532.         $$W{'uri'}=~s#\\$#/#;
  533.  
  534.     }
  535.  
  536.  
  537.  
  538.     # mode 1 - random URI (non-UTF8) encoding
  539.  
  540.     if($modes=~/1/){
  541.  
  542.         if($ENCODED==0){
  543.  
  544.             $$W{'uri'}=encode_str2ruri($$W{'uri'});
  545.  
  546.         $ENCODED=1;}
  547.  
  548.     }    
  549.  
  550.  
  551.  
  552.  
  553.  
  554.     # mode 5 - fake parameter
  555.  
  556.     if($modes=~/5/){ 
  557.  
  558.         ($s,$y)=(&utils_randstr,&utils_randstr); 
  559.  
  560.         $$W{'uri'}="/$s.html%3f$y=/../$$W{'uri'}";
  561.  
  562.     }
  563.  
  564.  
  565.  
  566.     # mode 3 - premature URL ending
  567.  
  568.     if($modes=~/3/){ 
  569.  
  570.         $s=&utils_randstr;
  571.  
  572.         $$W{'uri'}="/%20HTTP/1.1%0D%0A%0D%0AAccept%3A%20$s/../..$$W{'uri'}";
  573.  
  574.     }
  575.  
  576.     
  577.  
  578.     # mode 6 - TAB as request spacer
  579.  
  580.     if($modes=~/6/){
  581.  
  582.         $$W{'req_spacer'}="\t";
  583.  
  584.     }    
  585.  
  586.  
  587.  
  588. } # end anti_ids
  589.  
  590.  
  591.  
  592.  
  593.  
  594.  
  595.  
  596.  
  597.  
  598. =pod    
  599.  
  600.  
  601.  
  602.  
  603.  
  604. =head1 ++ Sub package: auth
  605.  
  606.         
  607.  
  608. The auth sub package implements HTTP authentication routines.
  609.  
  610.  
  611.  
  612. =cut
  613.  
  614.  
  615.  
  616. ########################################################################
  617.  
  618.  
  619.  
  620. =pod    
  621.  
  622.  
  623.  
  624. =head1 - Function: LW::auth_brute_force
  625.  
  626.         
  627.  
  628. Params: $auth_method, \%hin, $user, \@passwords [, $domain]
  629.  
  630. Return: $first_valid_password, undef if error/none found
  631.  
  632.  
  633.  
  634. Perform a HTTP authentication brute force against a server (host and URI 
  635.  
  636. defined in %hin).  It will try every password in the password array for 
  637.  
  638. the given user.  The first password (in conjunction with the given user) 
  639.  
  640. that doesn't return HTTP 401 is returned (and the brute force is stopped 
  641.  
  642. at that point).  $domain is optional, and is only used for NTLM auth.
  643.  
  644.  
  645.  
  646. =cut
  647.  
  648.  
  649.  
  650. sub auth_brute_force {
  651.  
  652.  my ($auth_method, $hrin, $user, $pwordref, $dom)=@_;
  653.  
  654.  my ($P,%hout);
  655.  
  656.  
  657.  
  658.  return undef if(!defined $auth_method || length($auth_method)==0);
  659.  
  660.  return undef if(!defined $user        || length($user)       ==0);
  661.  
  662.  return undef if(!(defined $hrin     && ref($hrin)    ));
  663.  
  664.  return undef if(!(defined $pwordref && ref($pwordref)));
  665.  
  666.  
  667.  
  668.  map {
  669.  
  670.     ($P=$_)=~tr/\r\n//d;
  671.  
  672.     auth_set_header($auth_method,$hrin,$user,$P,$dom);
  673.  
  674.     return undef if(http_do_request($hrin,\%hout));
  675.  
  676.     return $P if($hout{'whisker'}->{'http_resp'} ne 401);
  677.  
  678.  } @$pwordref;
  679.  
  680.  
  681.  
  682.  return undef;}
  683.  
  684.  
  685.  
  686.  
  687.  
  688. ########################################################################
  689.  
  690.  
  691.  
  692. =pod
  693.  
  694.  
  695.  
  696. =head1 - Function: LW::auth_set_header
  697.  
  698.  
  699.  
  700. Params: $auth_method, \%hin, $user, $password [, $domain]
  701.  
  702. Return: nothing (modifies %hin)
  703.  
  704.  
  705.  
  706. Set the appropriate authentication header in %hin.
  707.  
  708.  
  709.  
  710. NOTE: right now only BASIC and NTLM are supported.
  711.  
  712.  
  713.  
  714. =cut
  715.  
  716.  
  717.  
  718. sub auth_set_header {
  719.  
  720.  my ($method, $href, $user, $pass, $domain)=(lc(shift),@_);
  721.  
  722.  
  723.  
  724.  return if(!(defined $href && ref($href)));
  725.  
  726.  return if(!defined $user || !defined $pass);
  727.  
  728.  
  729.  
  730.  if($method eq 'basic'){
  731.  
  732.     $$href{'Authorization'}='Basic '.encode_base64($user.':'.$pass,'');
  733.  
  734.  }
  735.  
  736.  
  737.  
  738.  if($method eq 'proxy-basic'){
  739.  
  740.     $$href{'Proxy-Authorization'}='Basic '.encode_base64($user.':'.$pass,'');
  741.  
  742.  }
  743.  
  744.  
  745.  
  746.  if($method eq 'ntlm'){
  747.  
  748.     my $o=ntlm_new($user,$pass,$domain);
  749.  
  750.     $$href{'whisker'}->{'ntlm_obj'}=$o;
  751.  
  752.     $$href{'whisker'}->{'ntlm_step'}=0;
  753.  
  754.     $$href{'Authorization'}='NTLM '.ntlm_client($o);
  755.  
  756.  }
  757.  
  758.  
  759.  
  760. }
  761.  
  762.  
  763.  
  764.  
  765.  
  766. ########################################################################
  767.  
  768.  
  769.  
  770. =pod
  771.  
  772.  
  773.  
  774. =head1 - Function: LW::do_auth
  775.  
  776.  
  777.  
  778. Params: $auth_method, \%hin, $user, $password [, $domain]
  779.  
  780. Return: nothing (modifies %hin)
  781.  
  782.  
  783.  
  784. This is an alias for auth_set_header().
  785.  
  786.  
  787.  
  788. =cut
  789.  
  790.  
  791.  
  792. sub do_auth {
  793.  
  794.     goto &auth_set_header;
  795.  
  796. }
  797.  
  798.  
  799.  
  800. =pod    
  801.  
  802.  
  803.  
  804. =head1 ++ Sub package: bruteurl
  805.  
  806.  
  807.  
  808. The bruteurl sub package is used to perform a brute-force of HTTP 
  809.  
  810. requests on an array of string components.
  811.  
  812.  
  813.  
  814. =cut
  815.  
  816.  
  817.  
  818.  
  819.  
  820. =pod    
  821.  
  822.  
  823.  
  824. =head1 - Function: LW::bruteurl
  825.  
  826.  
  827.  
  828. Params: \%hin, $pre, $post, \@values_in, \@values_out
  829.  
  830. Return: Nothing (adds to @out)
  831.  
  832.         
  833.  
  834. Bruteurl will perform a brute force against the host/server specified in
  835.  
  836. %hin.  However, it will make one request per entry in @in, taking the
  837.  
  838. value and setting $hin{'whisker'}->{'uri'}= $pre.value.$post.  Any URI
  839.  
  840. responding with an HTTP 200 or 403 response is pushed into @out.  An
  841.  
  842. example of this would be to brute force usernames, putting a list of
  843.  
  844. common usernames in @in, setting $pre='/~' and $post='/'.
  845.  
  846.  
  847.  
  848. =cut
  849.  
  850. sub bruteurl {
  851.  
  852.  my ($hin, $upre, $upost, $arin, $arout)=@_;
  853.  
  854.  my ($U,%hout);
  855.  
  856.  
  857.  
  858.  return if(!(defined $hin   && ref($hin)  ));
  859.  
  860.  return if(!(defined $arin  && ref($arin) ));
  861.  
  862.  return if(!(defined $arout && ref($arout)));
  863.  
  864.  return if(!defined $upre  || length($upre) ==0);
  865.  
  866.  return if(!defined $upost || length($upost)==0);
  867.  
  868.  
  869.  
  870.  http_fixup_request($hin);
  871.  
  872.  
  873.  
  874.  map {
  875.  
  876.   ($U=$_)=~tr/\r\n//d; next if($U eq '');
  877.  
  878.   if(!http_do_request($hin,\%hout,{'uri'=>$upre.$U.$upost})){
  879.  
  880.     if(    $hout{'whisker'}->{'http_resp'}==200 ||
  881.  
  882.     $hout{'whisker'}->{'http_resp'}==403){
  883.  
  884.     push(@{$arout},$U);
  885.  
  886.     }
  887.  
  888.   }
  889.  
  890.  } @$arin;
  891.  
  892. }
  893.  
  894.  
  895.  
  896.  
  897.  
  898. =pod    
  899.  
  900.  
  901.  
  902. =head1 ++ Sub package: cookie
  903.  
  904.         
  905.  
  906. Cookie handling functions.
  907.  
  908.  
  909.  
  910. Cookies are stored in a "jar" (hash), indexed by cookie name.  The 
  911.  
  912. contents are an anonymous array:
  913.  
  914.  
  915.  
  916. $jar{'name'}=@( 'value', 'domain', 'path', 'expire', 'secure' )
  917.  
  918.  
  919.  
  920. =cut
  921.  
  922.  
  923.  
  924. ########################################################################
  925.  
  926.  
  927.  
  928. =pod    
  929.  
  930.  
  931.  
  932. =head1 - Function: LW::cookie_read
  933.  
  934.      
  935.  
  936. Params: \%jar, \%hout
  937.  
  938. Return: $num_of_cookies_read
  939.  
  940.  
  941.  
  942. Read in cookies from an %hout hash (HTTP response), and put them in %jar.
  943.  
  944.  
  945.  
  946. =cut
  947.  
  948.  
  949.  
  950. sub cookie_read {
  951.  
  952.  my ($count,$jarref,$href)=(0,@_);
  953.  
  954.  
  955.  
  956.  return 0 if(!(defined $jarref && ref($jarref)));
  957.  
  958.  return 0 if(!(defined $href   && ref($href)  ));
  959.  
  960.  
  961.  
  962.  my $target = utils_find_lowercase_key($href,'set-cookie');
  963.  
  964.  
  965.  
  966.  if(!defined $target){
  967.  
  968.     return 0;}
  969.  
  970.  
  971.  
  972.  if(ref($target)){ # multiple headers
  973.  
  974.     foreach (@{$target}){
  975.  
  976.         cookie_parse($jarref,$_);
  977.  
  978.         $count++; }
  979.  
  980.  } else { # single header
  981.  
  982.     cookie_parse($jarref,$target);
  983.  
  984.     $count=1; }
  985.  
  986.  
  987.  
  988.  return $count;
  989.  
  990. }
  991.  
  992.  
  993.  
  994.  
  995.  
  996. ########################################################################
  997.  
  998.  
  999.  
  1000. =pod    
  1001.  
  1002.  
  1003.  
  1004. =head1 - Function: LW::cookie_parse
  1005.  
  1006.      
  1007.  
  1008. Params: \%jar, $cookie
  1009.  
  1010. Return: nothing
  1011.  
  1012.  
  1013.  
  1014. Parses the cookie into the various parts and then sets the appropriate 
  1015.  
  1016. values in the %jar under the name; if the cookie is blank, it will delete 
  1017.  
  1018. it from the jar.
  1019.  
  1020.  
  1021.  
  1022. =cut
  1023.  
  1024.  
  1025.  
  1026. sub cookie_parse {
  1027.  
  1028.  my ($jarref, $header)=@_;
  1029.  
  1030.  my ($del,$part,@parts,@construct,$cookie_name)=(0);
  1031.  
  1032.  
  1033.  
  1034.  return if(!(defined $jarref && ref($jarref)));
  1035.  
  1036.  return if(!(defined $header && length($header)>0));
  1037.  
  1038.  
  1039.  
  1040.  @parts=split(/;/,$header);
  1041.  
  1042.  
  1043.  
  1044.  foreach $part (@parts){
  1045.  
  1046.     if($part=~/^[ \t]*(.+?)=(.*)$/){
  1047.  
  1048.         my ($name,$val)=($1,$2);
  1049.  
  1050.         if($name=~/^domain$/i){        
  1051.  
  1052.             $val=~s#^http://##;
  1053.  
  1054.             $val=~s#/.*$##;
  1055.  
  1056.             $construct[1]=$val;
  1057.  
  1058.         } elsif($name=~/^path$/i){
  1059.  
  1060.             $val=~s#/$## if($val ne '/');
  1061.  
  1062.             $construct[2]=$val;
  1063.  
  1064.         } elsif($name=~/^expires$/i){
  1065.  
  1066.             $construct[3]=$val;
  1067.  
  1068.         } else {
  1069.  
  1070.             $cookie_name=$name;
  1071.  
  1072.             if($val eq ''){        $del=1;
  1073.  
  1074.             } else {        $construct[0]=$val;}
  1075.  
  1076.         }    
  1077.  
  1078.     } else {
  1079.  
  1080.         if($part=~/secure/){
  1081.  
  1082.             $construct[4]=1;}
  1083.  
  1084.  }    }
  1085.  
  1086.  
  1087.  
  1088.  if($del){
  1089.  
  1090.       delete $$jarref{$cookie_name} if defined $$jarref{$cookie_name};
  1091.  
  1092.  } else {
  1093.  
  1094.     $$jarref{$cookie_name}=\@construct;
  1095.  
  1096.  }
  1097.  
  1098. }
  1099.  
  1100.  
  1101.  
  1102.  
  1103.  
  1104. ########################################################################
  1105.  
  1106.  
  1107.  
  1108. =pod    
  1109.  
  1110.  
  1111.  
  1112. =head1 - Function: LW::cookie_write
  1113.  
  1114.      
  1115.  
  1116. Params: \%jar, \%hin, $override
  1117.  
  1118. Return: nothing
  1119.  
  1120.  
  1121.  
  1122. Goes through the given jar and sets the Cookie header in %hin pending the 
  1123.  
  1124. correct domain and path.  If $override is true, then the domain and path
  1125.  
  1126. restrictions of the cookies are ignored.
  1127.  
  1128.  
  1129.  
  1130. Todo: factor in expire and secure.
  1131.  
  1132.  
  1133.  
  1134. =cut
  1135.  
  1136.  
  1137.  
  1138. sub cookie_write {
  1139.  
  1140.  my ($jarref, $hin, $override)=@_;
  1141.  
  1142.  my ($name,$out)=('','');
  1143.  
  1144.  
  1145.  
  1146.  return if(!(defined $jarref && ref($jarref)));
  1147.  
  1148.  return if(!(defined $hin    && ref($hin)   ));
  1149.  
  1150.  
  1151.  
  1152.  $override=$override||0;
  1153.  
  1154.  $$hin{'whisker'}->{'ssl'}=$$hin{'whisker'}->{'ssl'}||0;
  1155.  
  1156.  
  1157.  
  1158.  foreach $name (keys %$jarref){
  1159.  
  1160.     next if($name eq '');
  1161.  
  1162.     next if($$hin{'whisker'}->{'ssl'}==0 && $$jarref{$name}->[4]>0);
  1163.  
  1164.     if($override || 
  1165.  
  1166.           ($$hin{'whisker'}->{'host'}=~/$$jarref{$name}->[1]$/i &&
  1167.  
  1168.        $$hin{'whisker'}->{'uri'}=~/$$jarref{$name}->[2]/i)){
  1169.  
  1170.         $out.="$name=$$jarref{$name}->[0];";
  1171.  
  1172.  }    }
  1173.  
  1174.  
  1175.  
  1176.  if($out ne ''){ $$hin{'Cookie'}=$out; }
  1177.  
  1178.  
  1179.  
  1180. }
  1181.  
  1182.  
  1183.  
  1184.  
  1185.  
  1186. ########################################################################
  1187.  
  1188.  
  1189.  
  1190. =pod    
  1191.  
  1192.  
  1193.  
  1194. =head1 - Function: LW::cookie_get
  1195.  
  1196.      
  1197.  
  1198. Params: \%jar, $name
  1199.  
  1200. Return: @elements
  1201.  
  1202.  
  1203.  
  1204. Fetch the named cookie from the jar, and return the components.
  1205.  
  1206.  
  1207.  
  1208. =cut
  1209.  
  1210.  
  1211.  
  1212. sub cookie_get {
  1213.  
  1214.  my ($jarref,$name)=@_;
  1215.  
  1216.  
  1217.  
  1218.  return undef if(!(defined $jarref && ref($jarref)));
  1219.  
  1220.  
  1221.  
  1222.  if(defined $$jarref{$name}){
  1223.  
  1224.     return @{$$jarref{$name}};}
  1225.  
  1226.  
  1227.  
  1228.  return undef;
  1229.  
  1230. }
  1231.  
  1232.  
  1233.  
  1234.  
  1235.  
  1236. ########################################################################
  1237.  
  1238.  
  1239.  
  1240. =pod    
  1241.  
  1242.  
  1243.  
  1244. =head1 - Function: LW::cookie_set
  1245.  
  1246.      
  1247.  
  1248. Params: \%jar, $name, $value, $domain, $path, $expire, $secure
  1249.  
  1250. Return: nothing
  1251.  
  1252.  
  1253.  
  1254. Set the named cookie with the provided values into the %jar.
  1255.  
  1256.  
  1257.  
  1258. =cut
  1259.  
  1260.  
  1261.  
  1262. sub cookie_set {
  1263.  
  1264.  my ($jarref,$name,$value,$domain,$path,$expire,$secure)=@_;
  1265.  
  1266.  my @construct;
  1267.  
  1268.  
  1269.  
  1270.  return if(!(defined $jarref && ref($jarref)));
  1271.  
  1272.  
  1273.  
  1274.  return if($name eq '');
  1275.  
  1276.  if($value eq ''){
  1277.  
  1278.     delete $$jarref{$name};
  1279.  
  1280.     return;}
  1281.  
  1282.  $path=$path||'/';
  1283.  
  1284.  $secure=$secure||0;
  1285.  
  1286.  
  1287.  
  1288.  @construct=($value,$domain,$path,$expire,$secure);
  1289.  
  1290.  $$jarref{$name}=\@construct; 
  1291.  
  1292. }
  1293.  
  1294.  
  1295.  
  1296.  
  1297.  
  1298. ########################################################################
  1299.  
  1300.  
  1301.  
  1302.  
  1303.  
  1304. =pod
  1305.  
  1306.  
  1307.  
  1308. =head1 ++ Sub package: crawl
  1309.  
  1310.  
  1311.  
  1312. Used for crawling a website by requesting a (start) page, reading the
  1313.  
  1314. HTML, extracting the links, and then requesting those links--up to a
  1315.  
  1316. specified depth.  The module also allows various configuration tweaks to
  1317.  
  1318. do such things as monitor requests for offsite URLs (pages on other
  1319.  
  1320. hosts), track various cookies, etc.
  1321.  
  1322.  
  1323.  
  1324. =cut
  1325.  
  1326.  
  1327.  
  1328. #####################################################
  1329.  
  1330.  
  1331.  
  1332. =pod
  1333.  
  1334.  
  1335.  
  1336. =head1 - Function: LW::crawl
  1337.  
  1338.   
  1339.  
  1340. Params: $START, $MAX_DEPTH, \%tracking, \%hin
  1341.  
  1342. Return: Nothing
  1343.  
  1344.  
  1345.  
  1346. The heart of the crawl package.  Will perform an HTTP crawl on the
  1347.  
  1348. specified HOST, starting at START URI, proceeding up to MAX_DEPTH.  A
  1349.  
  1350. tracking hash reference (required) stores the results of each page (and
  1351.  
  1352. ongoing progress).  The http_in_options hash reference specifies a
  1353.  
  1354. standard HTTP hash for use in the outgoing HTTP requests.  Certain options
  1355.  
  1356. are configurable via LW::crawl_set_config().  The tracking hash will
  1357.  
  1358. contain all the pages visited; you can get the crawl engine to skip pages
  1359.  
  1360. by placing them in the tracking hash ahead of time.
  1361.  
  1362.  
  1363.  
  1364. START (first) parameter should be of the form "http://www.host.com/url".
  1365.  
  1366.  
  1367.  
  1368. =cut
  1369.  
  1370.  
  1371.  
  1372. sub crawl {
  1373.  
  1374.  my ($START, $MAX_DEPTH, $hrtrack, $hrin)=@_;
  1375.  
  1376.  my (%hout, %jar);
  1377.  
  1378.  my ($T, @ST, @links, @tlinks, @vals, @ERRORS)=('');
  1379.  
  1380.  
  1381.  
  1382.  return if(!(defined $hrtrack && ref($hrtrack)));
  1383.  
  1384.  return if(!(defined $hrin    && ref($hrin)   )); 
  1385.  
  1386.  return if(!defined $START || length($START)==0);
  1387.  
  1388.  
  1389.  
  1390.  $MAX_DEPTH||=2;
  1391.  
  1392.  
  1393.  
  1394.  # $ST[0]=HOST  $ST[1]=URL  $ST[2]=CWD  $ST[3]=HTTPS  $ST[4]=SERVER
  1395.  
  1396.  # $ST[5]=PORT  $ST[6]=DEPTH
  1397.  
  1398.  
  1399.  
  1400.  @vals=utils_split_uri($START);
  1401.  
  1402.  $ST[1]=$vals[0];     # uri
  1403.  
  1404.  $ST[0]=$vals[2];     # host
  1405.  
  1406.  $ST[5]=$vals[3];     # port
  1407.  
  1408.  $ST[4]=undef;        # server tag
  1409.  
  1410.  
  1411.  
  1412.  return if($ST[0] eq '');
  1413.  
  1414.  
  1415.  
  1416.  # some various informationz...
  1417.  
  1418.  $LW::crawl_config{'host'}=$ST[0];
  1419.  
  1420.  $LW::crawl_config{'port'}=$ST[5];
  1421.  
  1422.  $LW::crawl_config{'start'}=$ST[1];
  1423.  
  1424.  
  1425.  
  1426.  $$hrin{'whisker'}->{'host'}=$ST[0];
  1427.  
  1428.  $$hrin{'whisker'}->{'port'}=$ST[5];
  1429.  
  1430.  $$hrin{'whisker'}->{'lowercase_incoming_headers'}=1; # makes life easier
  1431.  
  1432.  
  1433.  
  1434.  http_fixup_request($hrin);
  1435.  
  1436.  
  1437.  
  1438.  # this is so callbacks can access internals via references
  1439.  
  1440.  $LW::crawl_config{'ref_links'}=\@links;
  1441.  
  1442.  $LW::crawl_config{'ref_jar'}=\%jar;
  1443.  
  1444.  $LW::crawl_config{'ref_hin'}=$hrin;
  1445.  
  1446.  $LW::crawl_config{'ref_hout'}=\%hout;
  1447.  
  1448.  
  1449.  
  1450.  %LW::crawl_referrers=(); # empty out existing referrers
  1451.  
  1452.  %LW::crawl_server_tags=();
  1453.  
  1454.  %LW::crawl_offsites=();
  1455.  
  1456.  %LW::crawl_cookies=();
  1457.  
  1458.  %LW::crawl_forms=();
  1459.  
  1460.  
  1461.  
  1462.  push @links, \@{[$ST[1],1,($vals[1] eq 'https')?1:0]};
  1463.  
  1464.  
  1465.  
  1466.  while(@links){
  1467.  
  1468.   my $C=shift @links;
  1469.  
  1470.   $ST[1]=$C->[0]; # url
  1471.  
  1472.   $ST[6]=$C->[1]; # depth
  1473.  
  1474.   $ST[3]=$C->[2]; # https
  1475.  
  1476.  
  1477.  
  1478.   next if(defined $$hrtrack{$ST[1]} && $$hrtrack{$ST[1]} ne '?');
  1479.  
  1480.  
  1481.  
  1482.   if($ST[6] > $MAX_DEPTH){
  1483.  
  1484.     $$hrtrack{$ST[1]}='?' if($LW::crawl_config{'save_skipped'}>0);
  1485.  
  1486.     next;
  1487.  
  1488.   }
  1489.  
  1490.  
  1491.  
  1492.   $ST[2]=utils_get_dir($ST[1]);
  1493.  
  1494.  
  1495.  
  1496.   $$hrin{'whisker'}->{'uri'}=$ST[1];
  1497.  
  1498.   $$hrin{'whisker'}->{'ssl'}=$ST[3];
  1499.  
  1500.   my $result = crawl_do_request($hrin,\%hout);
  1501.  
  1502.   if($result==1 || $result==2){
  1503.  
  1504.     push @ERRORS, "Error on making request for '$ST[1]': $hout{'whisker'}->{'error'}";
  1505.  
  1506.     next;
  1507.  
  1508.   }
  1509.  
  1510.  
  1511.  
  1512.   if($result==0 || $result==4){
  1513.  
  1514.     $$hrtrack{$ST[1]}=$hout{'whisker'}->{'http_resp'}; }
  1515.  
  1516.   
  1517.  
  1518.   if($result==3 || $result==5){
  1519.  
  1520.     $$hrtrack{$ST[1]}='?' if($LW::crawl_config{'save_skipped'}>0); }
  1521.  
  1522.  
  1523.  
  1524.   if(defined $hout{'server'}){ 
  1525.  
  1526.    if(!defined $ST[4]){ # server tag
  1527.  
  1528.     $ST[4]=$hout{'server'}; }
  1529.  
  1530.    $LW::crawl_server_tags{$hout{'server'}}++;
  1531.  
  1532.   }
  1533.  
  1534.  
  1535.  
  1536.   if(defined $hout{'set-cookie'}){
  1537.  
  1538.         if($LW::crawl_config{'save_cookies'}>0){
  1539.  
  1540.             if(ref($hout{'set-cookie'})){
  1541.  
  1542.                 foreach (@{$hout{'set-cookie'}}){
  1543.  
  1544.                     $LW::crawl_cookies{$_}++; }
  1545.  
  1546.             } else {
  1547.  
  1548.                 $LW::crawl_cookies{$hout{'set-cookie'}}++; 
  1549.  
  1550.         }    }
  1551.  
  1552.  
  1553.  
  1554.         if($LW::crawl_config{'reuse_cookies'}>0){
  1555.  
  1556.             cookie_read(\%jar,\%hout); }
  1557.  
  1558.   }
  1559.  
  1560.  
  1561.  
  1562.  
  1563.  
  1564.   next if($result==4 || $result==5);  
  1565.  
  1566.   next if(scalar @links > $LW::crawl_config{'url_limit'});
  1567.  
  1568.  
  1569.  
  1570.   if($result==0){ # page should be parsed
  1571.  
  1572.     if($LW::crawl_config{'source_callback'} != 0  &&
  1573.  
  1574.         ref($LW::crawl_config{'source_callback'})){
  1575.  
  1576.         &{$LW::crawl_config{'source_callback'}}($hrin,\%hout); }
  1577.  
  1578.  
  1579.  
  1580.     LW::html_find_tags(\$hout{'whisker'}->{'data'},
  1581.  
  1582.         \&crawl_extract_links_test);
  1583.  
  1584.     $LW::crawl_config{'stats_html'}++; # count how many pages we've parsed
  1585.  
  1586.   }
  1587.  
  1588.  
  1589.  
  1590.   if($result==3){ # follow the move via location header
  1591.  
  1592.     push @LW::crawl_urls, $hout{'location'}; }
  1593.  
  1594.  
  1595.  
  1596.   foreach $T (@LW::crawl_urls){
  1597.  
  1598.      $T=~tr/\0\r\n//d; # the NULL character is a bug that's somewhere
  1599.  
  1600.      next if (length($T)==0);
  1601.  
  1602.      next if ($T=~/^javascript:/i); # stupid javascript
  1603.  
  1604.      next if ($T=~/^mailto:/i);
  1605.  
  1606.      next if ($T=~m#^([a-zA-Z]*)://# && lc($1) ne 'http' && lc($1) ne 'https');
  1607.  
  1608.      next if ($T=~/^#/i); # fragment
  1609.  
  1610.  
  1611.  
  1612.      if($LW::crawl_config{'callback'} != 0){
  1613.  
  1614.         next if &{$LW::crawl_config{'callback'}}($T,@ST); }
  1615.  
  1616.  
  1617.  
  1618.      push(@{$LW::crawl_referrers{$T}}, $ST[1]) 
  1619.  
  1620.         if( $LW::crawl_config{'save_referrers'}>0 );
  1621.  
  1622.  
  1623.  
  1624.      $T=utils_absolute_uri($T,$ST[1],1) if($LW::crawl_config{'normalize_uri'}>0);
  1625.  
  1626.      @vals=utils_split_uri($T);
  1627.  
  1628.  
  1629.  
  1630.      # slashdot bug: workaround for the following fsck'd html code:
  1631.  
  1632.      # <FORM ACTION="//slashdot.org/users.pl" METHOD="GET">
  1633.  
  1634.      if($LW::crawl_config{'slashdot_bug'} > 0 && 
  1635.  
  1636.             substr($vals[0],0,2) eq '//'){
  1637.  
  1638.         if($ST[3]==1){    $T='https:'.$T;
  1639.  
  1640.         } else {    $T='http:' .$T; }
  1641.  
  1642.         @vals=utils_split_uri($T);
  1643.  
  1644.      }
  1645.  
  1646.  
  1647.  
  1648.      # make sure URL is on same host, port, and protocol
  1649.  
  1650.      if( (defined $vals[2] && $vals[2] ne $ST[0]) || 
  1651.  
  1652.             (defined $vals[3] && $vals[3] != $ST[5]) ||
  1653.  
  1654.             (defined $vals[1] && ($vals[1] ne 'http' 
  1655.  
  1656.                 && $vals[1] ne 'https'))){
  1657.  
  1658.         if($LW::crawl_config{'save_offsites'}>0){
  1659.  
  1660.             $LW::crawl_offsites{utils_join_uri(@vals)}++; }
  1661.  
  1662.         next; }
  1663.  
  1664.  
  1665.  
  1666.      if(substr($vals[0],0,1) ne '/'){
  1667.  
  1668.         $vals[0]=$ST[2].$vals[0]; }
  1669.  
  1670.  
  1671.  
  1672.      my $where=rindex($vals[0],'.');
  1673.  
  1674.      my $EXT='';
  1675.  
  1676.      if($where >= 0){
  1677.  
  1678.        $EXT = substr($vals[0], $where+1, length($vals[0])-$where); }
  1679.  
  1680.  
  1681.  
  1682.      $EXT=~tr/0-9a-zA-Z//cd; # yucky chars will puke regex below
  1683.  
  1684.  
  1685.  
  1686.      if($EXT ne '' && $LW::crawl_config{'skip_ext'}=~/\.$EXT /i){
  1687.  
  1688.         if($LW::crawl_config{'save_skipped'}>0){
  1689.  
  1690.             $$hrtrack{$vals[0]}='?'; }
  1691.  
  1692.          next; }
  1693.  
  1694.  
  1695.  
  1696.      if(defined $vals[4] && $LW::crawl_config{'use_params'}>0){
  1697.  
  1698.         if($LW::crawl_config{'params_double_record'}>0 &&
  1699.  
  1700.                 !defined $$hrtrack{$vals[0]}){
  1701.  
  1702.             $$hrtrack{$vals[0]}='?'; }
  1703.  
  1704.         $vals[0]=$vals[0].'?'.$vals[4];    
  1705.  
  1706.      }
  1707.  
  1708.  
  1709.  
  1710.      next if(defined $$hrtrack{$vals[0]});
  1711.  
  1712.  
  1713.  
  1714.      push @links, \@{[$vals[0],$ST[6]+1, ($vals[1] eq 'https')?1:0]};
  1715.  
  1716.  
  1717.  
  1718.   } # foreach
  1719.  
  1720.  
  1721.  
  1722.   @LW::crawl_urls=(); # reset for next round
  1723.  
  1724.  } # while
  1725.  
  1726.  
  1727.  
  1728.  my $key;
  1729.  
  1730.  foreach $key (keys %LW::crawl_config){
  1731.  
  1732.      delete $LW::crawl_config{$key} if (substr($key,0,4) eq 'ref_');}
  1733.  
  1734.  
  1735.  
  1736.  $LW::crawl_config{'stats_reqs'}=$hout{'whisker'}->{'stats_reqs'};
  1737.  
  1738.  $LW::crawl_config{'stats_syns'}=$hout{'whisker'}->{'stats_syns'};
  1739.  
  1740.  
  1741.  
  1742. } # end sub crawl
  1743.  
  1744.  
  1745.  
  1746. #####################################################
  1747.  
  1748.  
  1749.  
  1750. =pod
  1751.  
  1752.  
  1753.  
  1754. =head1 - Function: LW::crawl_get_config
  1755.  
  1756.   
  1757.  
  1758. Params: $config_directive
  1759.  
  1760. Return: $config_directive_value
  1761.  
  1762.  
  1763.  
  1764. Returns the set value of the submitted config_directive.  See
  1765.  
  1766. LW::crawl_set_config() for a list of configuration values.
  1767.  
  1768.  
  1769.  
  1770. =cut
  1771.  
  1772.  
  1773.  
  1774. sub crawl_get_config {
  1775.  
  1776.     my $key=shift;
  1777.  
  1778.     return $LW::crawl_config{$key};
  1779.  
  1780. }
  1781.  
  1782.  
  1783.  
  1784. #####################################################
  1785.  
  1786.  
  1787.  
  1788. =pod
  1789.  
  1790.  
  1791.  
  1792. =head1 - Function: LW::crawl_set_config
  1793.  
  1794.   
  1795.  
  1796. Params: $config_directive, $value
  1797.  
  1798. Return: Nothing
  1799.  
  1800.  
  1801.  
  1802. This function adjusts the configuration of the crawl package. Use values
  1803.  
  1804. of 0 and 1 for off and on, respectively.  The defaults are set in 
  1805.  
  1806. libs/globals.wpl.
  1807.  
  1808.  
  1809.  
  1810. save_cookies
  1811.  
  1812. - crawl will save all cookies encountered, for later review
  1813.  
  1814.  
  1815.  
  1816. save_offsite_urls
  1817.  
  1818. - crawl will save all offsite URLs (URLs not on this host); crawl
  1819.  
  1820.   will not actually crawl those hosts (use separate calls to crawl)
  1821.  
  1822.  
  1823.  
  1824. follow_moves
  1825.  
  1826. - crawl will follow the URL received from an HTTP move response
  1827.  
  1828.  
  1829.  
  1830. use_params
  1831.  
  1832. - crawl will factor in URI parameters when considering if a URI is unique 
  1833.  
  1834.   or not
  1835.  
  1836.  
  1837.  
  1838. params_double_record
  1839.  
  1840. - if both use_params and params_double_record are set, crawl will make two
  1841.  
  1842.   entries for each URI which has paramaters: one with and one without the
  1843.  
  1844.   parameters
  1845.  
  1846.  
  1847.  
  1848. reuse_cookies
  1849.  
  1850. - crawl will resubmit any received/prior cookies
  1851.  
  1852.  
  1853.  
  1854. skip_ext
  1855.  
  1856. - crawl will ignore requests for URLs ending in extensions given; the 
  1857.  
  1858.   value requires a specific string format: (dot)extension(space).  For
  1859.  
  1860.   example, to ignore GIFs and JPGs, you would run:
  1861.  
  1862.      LW::crawl_set_config('skip_ext',".gif .jpg ");
  1863.  
  1864.  
  1865.  
  1866. save_skipped
  1867.  
  1868. - any URLs that are skipped via skip_ext, or are above the specified DEPTH 
  1869.  
  1870.   will be recorded in the tracking hash with a value of '?' (instead of an
  1871.  
  1872.   HTTP response code).
  1873.  
  1874.  
  1875.  
  1876. callback
  1877.  
  1878. - crawl will call this function (if this is a reference to a function), 
  1879.  
  1880.   passing it the current URI and the @ST array (which has host, port, SSL, 
  1881.  
  1882.   etc info).  If the function returns a TRUE value, then crawl will skip
  1883.  
  1884.   that URI.  Set to value 0 (zero) if you do not want to use a callback.
  1885.  
  1886.  
  1887.  
  1888. slashdot_bug
  1889.  
  1890. - slashdot.org uses a screwy piece of invalid (yet it works) HTML in
  1891.  
  1892.   the form of <FORM ACTION="//slashdot.org/somefile">.  So basically,
  1893.  
  1894.   when a URL starts with '//' and slashdot_bug is set to 1 (which it
  1895.  
  1896.   is by default), then the proper 'http:' or 'https:' will be prepended
  1897.  
  1898.   to the URL.
  1899.  
  1900.  
  1901.  
  1902. source_callback
  1903.  
  1904. - crawl will call this function (if this is a reference to a function), 
  1905.  
  1906.   passing references to %hin and %hout, right before it parses the page
  1907.  
  1908.   for HTML links.  This allows the callback function to review or
  1909.  
  1910.   modify the HTML before it's parsed for links.  Return value is ignored.
  1911.  
  1912.   
  1913.  
  1914. url_limit
  1915.  
  1916. - number or URLs that crawl will queue up at one time; defaults to 1000
  1917.  
  1918.  
  1919.  
  1920. do_head
  1921.  
  1922. - use head requests to determine if a file has a content-type worth
  1923.  
  1924.   downloading.  Potentially saves some time, assuming the server properly
  1925.  
  1926.   supports HEAD requests.  Set to value 1 to use (0/off by default).
  1927.  
  1928.  
  1929.  
  1930.  
  1931.  
  1932. =cut
  1933.  
  1934.  
  1935.  
  1936. sub crawl_set_config {
  1937.  
  1938.     return if(!defined $_[0]);
  1939.  
  1940.     my %opts=@_;
  1941.  
  1942.     while( my($k,$v)=each %opts){
  1943.  
  1944.         $LW::crawl_config{lc($k)}=$v; }
  1945.  
  1946. }
  1947.  
  1948.  
  1949.  
  1950. #####################################################
  1951.  
  1952.  
  1953.  
  1954. =pod
  1955.  
  1956.  
  1957.  
  1958. =head1 - Function: LW::crawl_extract_links_test (INTERNAL)
  1959.  
  1960.   
  1961.  
  1962. Params: $TAG, \%elements, \$html_data, $offset, $len
  1963.  
  1964. Return: nothing
  1965.  
  1966.  
  1967.  
  1968. This is the callback function used by the crawl function, and passed to 
  1969.  
  1970. html_find_tags.  It will find URL/URI links and place them in 
  1971.  
  1972. @LW::crawl_urls.
  1973.  
  1974.  
  1975.  
  1976. =cut
  1977.  
  1978.  
  1979.  
  1980. sub crawl_extract_links_test {
  1981.  
  1982.     my ($TAG, $hr, $dr, $start, $len)=(lc(shift),@_);
  1983.  
  1984.     my $t;
  1985.  
  1986.  
  1987.  
  1988.     # this should be most of the time...
  1989.  
  1990.     return undef if(!defined ($t=$LW::crawl_linktags{$TAG}));
  1991.  
  1992.     return undef if(!scalar %$hr); # fastpath quickie
  1993.  
  1994.  
  1995.  
  1996.     while( my ($key,$val)= each %$hr){ # normalize element values
  1997.  
  1998.         $$hr{lc($key)} = $val;
  1999.  
  2000.     }
  2001.  
  2002.  
  2003.  
  2004.     if(ref($t)){
  2005.  
  2006.         foreach (@$t){
  2007.  
  2008.             push(@LW::crawl_urls,$$hr{$_}) if(defined $$hr{$_});
  2009.  
  2010.         }
  2011.  
  2012.     } else {
  2013.  
  2014.         push(@LW::crawl_urls,$$hr{$t}) if(defined $$hr{$t});
  2015.  
  2016.     }
  2017.  
  2018.  
  2019.  
  2020.     if($TAG eq 'form' && defined $$hr{action}){
  2021.  
  2022.         my $u=$LW::crawl_config{'ref_hout'}->{'whisker'}->{'uri'};
  2023.  
  2024.         $LW::crawl_forms{utils_absolute_uri($$hr{action},$u,1)}++;
  2025.  
  2026.     }
  2027.  
  2028.  
  2029.  
  2030.     return undef;
  2031.  
  2032. }
  2033.  
  2034.  
  2035.  
  2036. ################################################################
  2037.  
  2038.  
  2039.  
  2040. =pod
  2041.  
  2042.  
  2043.  
  2044. =head1 - Function: LW::crawl_make_request (INTERNAL)
  2045.  
  2046.   
  2047.  
  2048. Params: \%hin, \%hout
  2049.  
  2050. Return: $status_code
  2051.  
  2052.  
  2053.  
  2054. This is an internal function used by LW::crawl(), and is responsible for
  2055.  
  2056. making HTTP requests, including any HEAD pre-requests and following move
  2057.  
  2058. responses.  Status codes are:
  2059.  
  2060.     0    Success
  2061.  
  2062.     1    Error during request
  2063.  
  2064.     2    Error on connection setup
  2065.  
  2066.     3    Move request; follow Location header
  2067.  
  2068.     4    File not of text/htm(l) type
  2069.  
  2070.     5    File not available
  2071.  
  2072.  
  2073.  
  2074. =cut
  2075.  
  2076.  
  2077.  
  2078. sub crawl_do_request {
  2079.  
  2080.  my ($hrin,$hrout) = @_;
  2081.  
  2082.  my $ret;
  2083.  
  2084.  
  2085.  
  2086.  if($LW::crawl_config{'do_head'}){  
  2087.  
  2088.     my $save=$$hrin{'whisker'}->{'method'};
  2089.  
  2090.     $$hrin{'whisker'}->{'method'}='HEAD';
  2091.  
  2092.     $ret=http_do_request($hrin,$hrout);
  2093.  
  2094.     $$hrin{'whisker'}->{'method'}=$save;
  2095.  
  2096.  
  2097.  
  2098.     return 2 if($ret==2); # if there was connection error, do not continue
  2099.  
  2100.     if($ret==0){ # successful request
  2101.  
  2102.             if($$hrout{'whisker'}->{'http_resp'}==501){ # HEAD not allowed
  2103.  
  2104.                 $LW::crawl_config{'do_head'}=0; # no more HEAD requests
  2105.  
  2106.             }
  2107.  
  2108.  
  2109.  
  2110.         if($$hrout{'whisker'}->{'http_resp'} <308 &&
  2111.  
  2112.                 $$hrout{'whisker'}->{'http_resp'} >300){
  2113.  
  2114.             if($LW::crawl_config{'follow_moves'} >0){
  2115.  
  2116.                 return 3 if(defined $$hrout{'location'}); }
  2117.  
  2118.             return 5; # not avail
  2119.  
  2120.         }
  2121.  
  2122.  
  2123.  
  2124.         if($$hrout{'whisker'}->{'http_resp'}==200){
  2125.  
  2126.             # no content-type is treated as text/htm
  2127.  
  2128.             if(defined $$hrout{'content-type'} &&
  2129.  
  2130.                     $$hrout{'content-type'}!~/^text\/htm/i){
  2131.  
  2132.                 return 4;
  2133.  
  2134.             }        
  2135.  
  2136.             # fall through to GET request below            
  2137.  
  2138.         }
  2139.  
  2140.         }
  2141.  
  2142.     # request errors are essentially redone via GET, below
  2143.  
  2144.   }
  2145.  
  2146.  
  2147.  
  2148.  return http_do_request($hrin,$hrout);
  2149.  
  2150. }
  2151.  
  2152.  
  2153.  
  2154. #####################################################
  2155.  
  2156.  
  2157.  
  2158. =pod
  2159.  
  2160.  
  2161.  
  2162. =head1 ++ Sub package: dump
  2163.  
  2164.  
  2165.  
  2166. The dump subpackage contains various utility functions which emulate
  2167.  
  2168. the basic functionality provided by Data::Dumper.
  2169.  
  2170.  
  2171.  
  2172. =cut
  2173.  
  2174.  
  2175.  
  2176. ########################################################################
  2177.  
  2178.  
  2179.  
  2180. =pod
  2181.  
  2182.  
  2183.  
  2184. =head1 - Function: LW::dumper
  2185.  
  2186.   
  2187.  
  2188. Params: $name, \@array [, $name, \%hash, $name, \$scalar ]
  2189.  
  2190. Return: $code, undef on error
  2191.  
  2192.  
  2193.  
  2194. The dumper function will take the given $name and data reference, and
  2195.  
  2196. will create an ASCII perl code representation suitable for eval'ing
  2197.  
  2198. later to recreate the same structure.  $name is the name of the variable
  2199.  
  2200. that it will be saved as.  Example:
  2201.  
  2202.  
  2203.  
  2204.     $output = LW::dumper('hin',\%hin);
  2205.  
  2206.  
  2207.  
  2208. NOTE: dumper() creates anonymous structures under the name given.  For
  2209.  
  2210. example, if you dump the hash %hin under the name 'hin', then when you
  2211.  
  2212. eval the dumped code you will need to use %$hin, since $hin is now a
  2213.  
  2214. *reference* to a hash.
  2215.  
  2216.  
  2217.  
  2218. =cut
  2219.  
  2220.  
  2221.  
  2222. sub dumper {
  2223.  
  2224.     my %what=@_;
  2225.  
  2226.     my ($final,$k,$v)=('');
  2227.  
  2228.     while( ($k,$v)=each %what){
  2229.  
  2230.         return undef if(ref($k) || !ref($v));
  2231.  
  2232.         $final.="\$$k = "._dump(1,$v,1);
  2233.  
  2234.         $final=~s#,\n$##;
  2235.  
  2236.         $final.=";\n"; }
  2237.  
  2238.     return $final;
  2239.  
  2240. }
  2241.  
  2242.  
  2243.  
  2244. ########################################################################
  2245.  
  2246.  
  2247.  
  2248. =pod
  2249.  
  2250.  
  2251.  
  2252. =head1 - Function: LW::dumper_writefile
  2253.  
  2254.   
  2255.  
  2256. Params: $file, $name, \@array [, $name, \%hash, $name, \@scalar ]
  2257.  
  2258. Return: 0 if success; 1 if error
  2259.  
  2260.  
  2261.  
  2262. This calls dumper() and saves the output to the specified $file.  
  2263.  
  2264.  
  2265.  
  2266. Note: LW does not checking on the validity of the file name, it's
  2267.  
  2268. creation, or anything of the sort.  Files are opened in overwrite
  2269.  
  2270. mode.
  2271.  
  2272.  
  2273.  
  2274. =cut
  2275.  
  2276.  
  2277.  
  2278. sub dumper_writefile {
  2279.  
  2280.     my $file=shift;
  2281.  
  2282.     my $output=dumper(@_);
  2283.  
  2284.     return 1 if(!open(OUT,">$file") || $output eq 'ERROR');
  2285.  
  2286.     print OUT $output;
  2287.  
  2288.     close(OUT);
  2289.  
  2290. }
  2291.  
  2292.  
  2293.  
  2294. ########################################################################
  2295.  
  2296.  
  2297.  
  2298. =pod
  2299.  
  2300.  
  2301.  
  2302. =head1 - Function: LW::_dump (INTERNAL)
  2303.  
  2304.    
  2305.  
  2306. Params: $tabs, $ref
  2307.  
  2308. Return: $output
  2309.  
  2310.  
  2311.  
  2312. This is an internal function to dumper() which will dereference all
  2313.  
  2314. elements and produce the resulting code.
  2315.  
  2316.  
  2317.  
  2318. This function is not intended for external use.
  2319.  
  2320.  
  2321.  
  2322. =cut
  2323.  
  2324.  
  2325.  
  2326. sub _dump { # dereference and dump an element
  2327.  
  2328.     my ($t, $ref, $depth)=@_;
  2329.  
  2330.     my ($out,$k,$v)=('');
  2331.  
  2332.     $depth||=1;
  2333.  
  2334.  
  2335.  
  2336.     # to protect against circular loops
  2337.  
  2338.     return 'undef' if($depth > 128);
  2339.  
  2340.  
  2341.  
  2342.     if(!defined $ref){
  2343.  
  2344.         return 'undef';
  2345.  
  2346.     } elsif(ref($ref) eq 'HASH'){
  2347.  
  2348.         $out.="{\n";
  2349.  
  2350.         while( ($k,$v)=each %$ref){
  2351.  
  2352.             next if($k eq '');
  2353.  
  2354.             $out.= "\t"x$t;
  2355.  
  2356.             $out.=_dumpd($k).' => ';
  2357.  
  2358.             if(ref($v)){ $out.=_dump($t+1,$v,$depth+1); }
  2359.  
  2360.             else { $out.=_dumpd($v); }
  2361.  
  2362.             $out.=",\n" unless( substr($out,-2,2) eq ",\n");
  2363.  
  2364.         }
  2365.  
  2366.         $out=~s#,\n$#\n#;
  2367.  
  2368.         $out.="\t"x($t-1);
  2369.  
  2370.         $out.="},\n";
  2371.  
  2372.     } elsif(ref($ref) eq 'ARRAY'){
  2373.  
  2374.         $out.="[";
  2375.  
  2376.         if(~~@$ref){
  2377.  
  2378.             $out.="\n";
  2379.  
  2380.             foreach $v (@$ref) {
  2381.  
  2382.                 $out.= "\t"x$t;
  2383.  
  2384.                 if(ref($v)){ $out.=_dump($t+1,$v,$depth+1); }
  2385.  
  2386.                 else {       $out.=_dumpd($v); }
  2387.  
  2388.                 $out.=",\n" unless( substr($out,-2,2) eq ",\n");
  2389.  
  2390.             }
  2391.  
  2392.             $out=~s#,\n$#\n#;
  2393.  
  2394.             $out.="\t"x($t-1);
  2395.  
  2396.         }
  2397.  
  2398.         $out.="],\n";
  2399.  
  2400.     } elsif(ref($ref) eq 'SCALAR'){
  2401.  
  2402.         $out.=_dumpd($$ref);
  2403.  
  2404.     } elsif(ref($ref) eq 'REF'){
  2405.  
  2406.         $out.=_dump($t,$$ref,$depth+1);
  2407.  
  2408.     } elsif(ref($ref)){ # unknown/unsupported ref
  2409.  
  2410.         $out.="undef";
  2411.  
  2412.     } else { # normal scalar
  2413.  
  2414.         $out.=_dumpd($ref);
  2415.  
  2416.     }
  2417.  
  2418.     return $out;
  2419.  
  2420. }
  2421.  
  2422.  
  2423.  
  2424.  
  2425.  
  2426. ########################################################################
  2427.  
  2428.  
  2429.  
  2430. =pod
  2431.  
  2432.  
  2433.  
  2434. =head1 - Function: LW::_dumpd (INTERNAL)
  2435.  
  2436.    
  2437.  
  2438. Params: $string
  2439.  
  2440. Return: $escaped_string
  2441.  
  2442.  
  2443.  
  2444. This is an internal function to dumper() which will escape the given
  2445.  
  2446. string to make it suitable for printing.
  2447.  
  2448.  
  2449.  
  2450. This function is not intended for external use.
  2451.  
  2452.  
  2453.  
  2454. =cut
  2455.  
  2456.  
  2457.  
  2458. sub _dumpd { # escape a scalar string
  2459.  
  2460.     my $v=shift;
  2461.  
  2462.     return 'undef' if(!defined $v);
  2463.  
  2464.     return "''" if($v eq '');
  2465.  
  2466.     return "$v" if($v!~tr/0-9//c);
  2467.  
  2468.     return "'$v'" if($v!~tr/ !-~//c);
  2469.  
  2470.     $v=~s#\\#\\\\#g;    $v=~s#"#\\"#g;
  2471.  
  2472.     $v=~s#\r#\\r#g;        $v=~s#\n#\\n#g;
  2473.  
  2474.     $v=~s#\0#\\0#g;        $v=~s#\t#\\t#g;
  2475.  
  2476.     $v=~s#([^!-~ ])#sprintf('\\x%02x',ord($1))#eg;
  2477.  
  2478.     return "\"$v\"";
  2479.  
  2480. }
  2481.  
  2482.  
  2483.  
  2484. ########################################################################
  2485.  
  2486. =pod
  2487.  
  2488.  
  2489.  
  2490. =head1 ++ Sub package: easy
  2491.  
  2492.  
  2493.  
  2494. The 'easy' subpackage contains many high-level/simple functions to
  2495.  
  2496. do basic web tasks.  This should make it easier to use libwhisker
  2497.  
  2498. to do basic tasks.
  2499.  
  2500.  
  2501.  
  2502. =cut
  2503.  
  2504.  
  2505.  
  2506. ########################################################################
  2507.  
  2508.  
  2509.  
  2510. =pod
  2511.  
  2512.  
  2513.  
  2514. =head1 - Function: LW::get_page
  2515.  
  2516.   
  2517.  
  2518. Params: $url [, \%hin_request]
  2519.  
  2520. Return: $code, $data ($code will be set to undef on error, $data will
  2521.  
  2522.             contain error message)
  2523.  
  2524.  
  2525.  
  2526. This function will fetch the page at the given URL, and return the HTTP response code
  2527.  
  2528. and page contents.  Use this in the form of:
  2529.  
  2530. ($code,$html)=LW::get_page("http://host.com/page.html")
  2531.  
  2532.  
  2533.  
  2534. The optional %hin_request will be used if supplied.  This allows you to set
  2535.  
  2536. headers and other parameters.
  2537.  
  2538.  
  2539.  
  2540. =cut
  2541.  
  2542.  
  2543.  
  2544. sub get_page {
  2545.  
  2546.     my ($URL,$hr)=(shift,shift);
  2547.  
  2548.     return (undef,"No URL supplied") if(length($URL)==0);
  2549.  
  2550.  
  2551.  
  2552.     my (%req,%resp);
  2553.  
  2554.     my $rptr;
  2555.  
  2556.  
  2557.  
  2558.     if(defined $hr && ref($hr)){
  2559.  
  2560.         $rptr=$hr;
  2561.  
  2562.     } else {
  2563.  
  2564.         $rptr=\%req;
  2565.  
  2566.         LW::http_init_request(\%req);
  2567.  
  2568.     }
  2569.  
  2570.  
  2571.  
  2572.     LW::utils_split_uri($URL,$rptr); # this is newer >=1.1 syntax
  2573.  
  2574.     LW::http_fixup_request($rptr);
  2575.  
  2576.  
  2577.  
  2578.     if(http_do_request($rptr,\%resp)){
  2579.  
  2580.         return (undef,$resp{'whisker'}->{'error'});
  2581.  
  2582.     }
  2583.  
  2584.  
  2585.  
  2586.     return ($resp{'whisker'}->{'code'}, $resp{'whisker'}->{'data'});
  2587.  
  2588. }
  2589.  
  2590.  
  2591.  
  2592. ########################################################################
  2593.  
  2594.  
  2595.  
  2596. =pod
  2597.  
  2598.  
  2599.  
  2600. =head1 - Function: LW::get_page_hash
  2601.  
  2602.   
  2603.  
  2604. Params: $url [, \%hin_request]
  2605.  
  2606. Return: $hash_ref (undef on no URL)
  2607.  
  2608.  
  2609.  
  2610. This function will fetch the page at the given URL, and return the whisker
  2611.  
  2612. HTTP response hash.  The return code of the function is set to
  2613.  
  2614. $hash_ref->{whisker}->{get_page_hash}, and uses the LW::http_do_request()
  2615.  
  2616. response values.
  2617.  
  2618.  
  2619.  
  2620. Note: undef is returned if no URL is supplied
  2621.  
  2622.  
  2623.  
  2624. =cut
  2625.  
  2626.  
  2627.  
  2628. sub get_page_hash {
  2629.  
  2630.     my ($URL,$hr)=(shift,shift);
  2631.  
  2632.     return undef if(length($URL)==0);
  2633.  
  2634.  
  2635.  
  2636.     my (%req,%resp);
  2637.  
  2638.     my $rptr;
  2639.  
  2640.  
  2641.  
  2642.     if(defined $hr && ref($hr)){
  2643.  
  2644.         $rptr=$hr;
  2645.  
  2646.     } else {
  2647.  
  2648.         $rptr=\%req;
  2649.  
  2650.         LW::http_init_request(\%req);
  2651.  
  2652.     }
  2653.  
  2654.  
  2655.  
  2656.     LW::utils_split_uri($URL,$rptr); # this is newer >=1.1 syntax
  2657.  
  2658.     LW::http_fixup_request($rptr);
  2659.  
  2660.  
  2661.  
  2662.     my $r=http_do_request($rptr,\%resp);
  2663.  
  2664.     $resp{whisker}->{get_page_hash}=$r;
  2665.  
  2666.  
  2667.  
  2668.     return \%resp;
  2669.  
  2670. }
  2671.  
  2672.  
  2673.  
  2674. ########################################################################
  2675.  
  2676.  
  2677.  
  2678. =pod
  2679.  
  2680.  
  2681.  
  2682. =head1 - Function: LW::get_page_to_file
  2683.  
  2684.   
  2685.  
  2686. Params: $url, $filepath [, \%hin_request]
  2687.  
  2688. Return: $code ($code will be set to undef on error)
  2689.  
  2690.  
  2691.  
  2692. This function will fetch the page at the given URL, place the resulting HTML
  2693.  
  2694. in the file specified, and return the HTTP response code.  The optional
  2695.  
  2696. %hin_request hash sets the default parameters to be used in the request.
  2697.  
  2698.  
  2699.  
  2700. NOTE: libwhisker does not do any file checking; libwhisker will open the
  2701.  
  2702. supplied filepath for writing, overwriting any previously-existing files.
  2703.  
  2704. Libwhisker does not differentiate between a bad request, and a bad file
  2705.  
  2706. open.  If you're having troubles making this function work, make sure
  2707.  
  2708. that your $filepath is legal and valid, and that you have appropriate
  2709.  
  2710. write permissions to create/overwrite that file.
  2711.  
  2712.  
  2713.  
  2714. =cut
  2715.  
  2716.  
  2717.  
  2718. sub get_page_to_file {
  2719.  
  2720.     my ($URL, $filepath, $hr)=@_;
  2721.  
  2722.  
  2723.  
  2724.     return undef if(length($URL)==0);
  2725.  
  2726.     return undef if(length($filepath)==0);
  2727.  
  2728.  
  2729.  
  2730.     my (%req,%resp);
  2731.  
  2732.     my $rptr;
  2733.  
  2734.  
  2735.  
  2736.     if(defined $hr && ref($hr)){
  2737.  
  2738.         $rptr=$hr;
  2739.  
  2740.     } else {
  2741.  
  2742.         $rptr=\%req;
  2743.  
  2744.         LW::http_init_request(\%req);
  2745.  
  2746.     }
  2747.  
  2748.  
  2749.  
  2750.     LW::utils_split_uri($URL,$rptr); # this is newer >=1.1 syntax
  2751.  
  2752.     LW::http_fixup_request($rptr);
  2753.  
  2754.  
  2755.  
  2756.     if(http_do_request($rptr,\%resp)){
  2757.  
  2758.         return undef;
  2759.  
  2760.     }
  2761.  
  2762.     open(OUT,">$filepath") || return undef;
  2763.  
  2764.     binmode(OUT); # stupid Windows
  2765.  
  2766.     print OUT $resp{'whisker'}->{'data'};
  2767.  
  2768.     close(OUT);
  2769.  
  2770.  
  2771.  
  2772.     return $resp{'whisker'}->{'code'};
  2773.  
  2774. }
  2775.  
  2776.  
  2777.  
  2778. ########################################################################
  2779.  
  2780.  
  2781.  
  2782. =pod
  2783.  
  2784.  
  2785.  
  2786. =head1 - Function: LW::upload_file
  2787.  
  2788.   
  2789.  
  2790. Params: $url, $filepath, $paramname [, \%hin_request]
  2791.  
  2792. Return: $code ($code will be set to undef on error)
  2793.  
  2794.  
  2795.  
  2796. This function will upload the specified $file to the given $url as
  2797.  
  2798. the parameter named $paramname via a multipart POST request.  The 
  2799.  
  2800. optional $hin_request hash lets you set any other particular request
  2801.  
  2802. parameters.
  2803.  
  2804.  
  2805.  
  2806. NOTE: this is a highly simplied function for basic uploads.  If you
  2807.  
  2808. need to do more advanced things like set other multipart form
  2809.  
  2810. parameters, send multiple files, etc, then you will need to use the
  2811.  
  2812. normal API to do it yourself.
  2813.  
  2814.  
  2815.  
  2816. =cut
  2817.  
  2818.  
  2819.  
  2820. sub upload_file {
  2821.  
  2822.     my ($URL, $filepath, $paramname, $hr)=@_;
  2823.  
  2824.  
  2825.  
  2826.     return undef if(length($URL)      ==0);
  2827.  
  2828.     return undef if(length($filepath) ==0);
  2829.  
  2830.     return undef if(length($paramname)==0);
  2831.  
  2832.     return undef if(!(-e $filepath && -f $filepath));
  2833.  
  2834.  
  2835.  
  2836.     my (%req,%resp,%multi);
  2837.  
  2838.     my $rptr;
  2839.  
  2840.  
  2841.  
  2842.     if(defined $hr && ref($hr)){
  2843.  
  2844.         $rptr=$hr;
  2845.  
  2846.     } else {
  2847.  
  2848.         $rptr=\%req;
  2849.  
  2850.         LW::http_init_request(\%req);
  2851.  
  2852.     }
  2853.  
  2854.  
  2855.  
  2856.     LW::utils_split_uri($URL,$rptr); # this is newer >=1.1 syntax
  2857.  
  2858.     $rptr{'whisker'}->{'method'}='POST';
  2859.  
  2860.     LW::http_fixup_request($rptr);
  2861.  
  2862.  
  2863.  
  2864.     LW::multipart_setfile(\%multi,$filepath,$paramname);
  2865.  
  2866.     LW::multipart_write(\%multi,$rptr);
  2867.  
  2868.  
  2869.  
  2870.     if(http_do_request($rptr,\%resp)){
  2871.  
  2872.         return undef;
  2873.  
  2874.     }
  2875.  
  2876.  
  2877.  
  2878.     return $resp{'whisker'}->{'code'};
  2879.  
  2880. }
  2881.  
  2882.  
  2883.  
  2884. ########################################################################
  2885.  
  2886.  
  2887.  
  2888. =pod
  2889.  
  2890.  
  2891.  
  2892. =head1 - Function: LW::download_file
  2893.  
  2894.   
  2895.  
  2896. Params: $url, $filepath [, \%hin_request]
  2897.  
  2898. Return: $code ($code will be set to undef on error)
  2899.  
  2900.  
  2901.  
  2902. LW::download_file is just an alias for LW::get_page_to_file().
  2903.  
  2904.  
  2905.  
  2906. =cut
  2907.  
  2908.  
  2909.  
  2910. sub download_file {
  2911.  
  2912.     goto &LW::get_page_to_file;
  2913.  
  2914. }
  2915.  
  2916.  
  2917.  
  2918. ########################################################################
  2919.  
  2920.  
  2921.  
  2922.  
  2923.  
  2924. =pod    
  2925.  
  2926.  
  2927.  
  2928. =head1 ++ Sub package: encode
  2929.  
  2930.  
  2931.  
  2932. Various type encodings.  Installing MIME::Base64 will result in a 
  2933.  
  2934. compiled C version of base64 functions, which means they will be tons 
  2935.  
  2936. faster.  This is useful if you're going to run a Basic authentication 
  2937.  
  2938. brute force, which requires a high processing speed.  However, it's not 
  2939.  
  2940. required, since I include a Perl version, which is slower.
  2941.  
  2942.  
  2943.  
  2944. =cut
  2945.  
  2946.  
  2947.  
  2948. ########################################################################
  2949.  
  2950.  
  2951.  
  2952. =pod    
  2953.  
  2954.  
  2955.  
  2956. =head1 - Function: LW::encode_base64
  2957.  
  2958.   
  2959.  
  2960. Params: $data, $eol
  2961.  
  2962. Return: $base64_encoded_data
  2963.  
  2964.         
  2965.  
  2966. LW::encode_base64 is a stub function which will choose the fastest
  2967.  
  2968. function available for doing base64 encoding.  This is done by checking to
  2969.  
  2970. see if the MIME::Base64 perl module is available (which uses fast C
  2971.  
  2972. routines).  If it's not, then it defaults to a perl version (which is
  2973.  
  2974. slower).  You can call the perl version direct, but I suggest using the
  2975.  
  2976. stub to gain speed advantages where possible.
  2977.  
  2978.  
  2979.  
  2980. =cut
  2981.  
  2982.  
  2983.  
  2984. #sub encode_base64;
  2985.  
  2986.  
  2987.  
  2988.  
  2989.  
  2990. ########################################################################
  2991.  
  2992.  
  2993.  
  2994. =pod    
  2995.  
  2996.  
  2997.  
  2998. =head1 - Function: LW::decode_base64
  2999.  
  3000.   
  3001.  
  3002. Params: $data
  3003.  
  3004. Return: $base64_decoded_data
  3005.  
  3006.         
  3007.  
  3008. LW::decode_base64 is a stub function which will choose the fastest
  3009.  
  3010. function available for doing base64 decoding.  This is done by checking to
  3011.  
  3012. see if the MIME::Base64 perl module is available (which uses fast C
  3013.  
  3014. routines).  If it's not, then it defaults to a perl version (which is
  3015.  
  3016. slower).  You can call the perl version direct, but I suggest using the
  3017.  
  3018. stub to gain speed advantages where possible.
  3019.  
  3020.  
  3021.  
  3022. =cut
  3023.  
  3024.  
  3025.  
  3026. #sub decode_base64;
  3027.  
  3028.  
  3029.  
  3030.  
  3031.  
  3032. ########################################################################
  3033.  
  3034.  
  3035.  
  3036. =pod    
  3037.  
  3038.  
  3039.  
  3040. =head1 - Function: LW::encode_base64_perl
  3041.  
  3042.         
  3043.  
  3044. Params: $data, $eol
  3045.  
  3046. Return: $b64_encoded_data
  3047.  
  3048.  
  3049.  
  3050. A perl implementation of base64 encoding.  I recommend you use
  3051.  
  3052. LW::encode_base64 instead, since it may use the MIME::Base64 module (if
  3053.  
  3054. available), which lead to speed advantages.  The perl code for this
  3055.  
  3056. function was actually taken from an older MIME::Base64 perl module, and
  3057.  
  3058. bears the following copyright:
  3059.  
  3060.  
  3061.  
  3062. Copyright 1995-1999 Gisle Aas <gisle@aas.no>
  3063.  
  3064.  
  3065.  
  3066. NOTE: the $eol parameter will be inserted every 76 characters.  This is
  3067.  
  3068. used to format the data for output on a 80 character wide terminal.
  3069.  
  3070.  
  3071.  
  3072. =cut
  3073.  
  3074.  
  3075.  
  3076. sub encode_base64_perl { # ripped from MIME::Base64
  3077.  
  3078.     my $res = "";
  3079.  
  3080.     my $eol = $_[1];
  3081.  
  3082.     $eol = "\n" unless defined $eol;
  3083.  
  3084.     pos($_[0]) = 0;
  3085.  
  3086.     while ($_[0] =~ /(.{1,45})/gs) {
  3087.  
  3088.         $res .= substr(pack('u', $1), 1);
  3089.  
  3090.         chop($res);}
  3091.  
  3092.     $res =~ tr|` -_|AA-Za-z0-9+/|;
  3093.  
  3094.     my $padding = (3 - length($_[0]) % 3) % 3;
  3095.  
  3096.     $res =~ s/.{$padding}$/'=' x $padding/e if $padding;
  3097.  
  3098.     if (length $eol) {
  3099.  
  3100.         $res =~ s/(.{1,76})/$1$eol/g;
  3101.  
  3102.     } $res; }
  3103.  
  3104.  
  3105.  
  3106.  
  3107.  
  3108. ########################################################################
  3109.  
  3110.  
  3111.  
  3112. =pod    
  3113.  
  3114.  
  3115.  
  3116. =head1 - Function: LW::decode_base64_perl
  3117.  
  3118.   
  3119.  
  3120. Params: $data
  3121.  
  3122. Return: $b64_decoded_data
  3123.  
  3124.  
  3125.  
  3126. A perl implementation of base64 decoding.  The perl code for this function
  3127.  
  3128. was actually taken from an older MIME::Base64 perl module, and bears the 
  3129.  
  3130. following copyright:
  3131.  
  3132.  
  3133.  
  3134. Copyright 1995-1999 Gisle Aas <gisle@aas.no>
  3135.  
  3136.  
  3137.  
  3138. =cut
  3139.  
  3140.  
  3141.  
  3142. sub decode_base64_perl { # ripped from MIME::Base64
  3143.  
  3144.     my $str = shift;
  3145.  
  3146.     my $res = "";
  3147.  
  3148.     $str =~ tr|A-Za-z0-9+=/||cd;
  3149.  
  3150.     $str =~ s/=+$//;                        # remove padding
  3151.  
  3152.     $str =~ tr|A-Za-z0-9+/| -_|;            # convert to uuencoded format
  3153.  
  3154.     while ($str =~ /(.{1,60})/gs) {
  3155.  
  3156.         my $len = chr(32 + length($1)*3/4); # compute length byte
  3157.  
  3158.         $res .= unpack("u", $len . $1 );    # uudecode
  3159.  
  3160.     }$res;}
  3161.  
  3162.  
  3163.  
  3164.  
  3165.  
  3166. ########################################################################
  3167.  
  3168.  
  3169.  
  3170. =pod    
  3171.  
  3172.  
  3173.  
  3174. =head1 - Function: LW::encode_str2uri
  3175.  
  3176.   
  3177.  
  3178. Params: $data
  3179.  
  3180. Return: $result
  3181.  
  3182.  
  3183.  
  3184. This function encodes every character (except the / character) with normal 
  3185.  
  3186. URL hex encoding.
  3187.  
  3188.  
  3189.  
  3190. =cut
  3191.  
  3192.  
  3193.  
  3194. sub encode_str2uri { # normal hex encoding
  3195.  
  3196.     my $str=shift;
  3197.  
  3198.     $str=~s/([^\/])/sprintf("%%%02x",ord($1))/ge;
  3199.  
  3200.     return $str;}
  3201.  
  3202.  
  3203.  
  3204.  
  3205.  
  3206. #########################################################################
  3207.  
  3208.  
  3209.  
  3210. =pod    
  3211.  
  3212.  
  3213.  
  3214. =head1 - Function: LW::encode_str2ruri
  3215.  
  3216.   
  3217.  
  3218. Params: $data
  3219.  
  3220. Return: $result
  3221.  
  3222.  
  3223.  
  3224. This function randomly encodes characters (except the / character) with 
  3225.  
  3226. normal URL hex encoding.
  3227.  
  3228.  
  3229.  
  3230. =cut
  3231.  
  3232.  
  3233.  
  3234. sub encode_str2ruri { # random normal hex encoding
  3235.  
  3236.     my @T=split(//,shift);
  3237.  
  3238.     my $s;
  3239.  
  3240.     foreach (@T) {
  3241.  
  3242.      if(m#;=:&@\?#){
  3243.  
  3244.         $s.=$_;
  3245.  
  3246.         next;
  3247.  
  3248.       }
  3249.  
  3250.       if((rand()*2)%2 == 1){    $s.=sprintf("%%%02x",ord($_)) ;
  3251.  
  3252.       }else{            $s.=$_; }
  3253.  
  3254.     }
  3255.  
  3256.     return $s;
  3257.  
  3258. }
  3259.  
  3260.  
  3261.  
  3262. #########################################################################
  3263.  
  3264.  
  3265.  
  3266. =pod    
  3267.  
  3268.  
  3269.  
  3270. =head1 - Function: LW::encode_unicode
  3271.  
  3272.   
  3273.  
  3274. Params: $data
  3275.  
  3276. Return: $result
  3277.  
  3278.  
  3279.  
  3280. This function converts a normal string into Windows unicode format.
  3281.  
  3282.  
  3283.  
  3284. =cut
  3285.  
  3286.  
  3287.  
  3288. sub encode_unicode
  3289.  
  3290. {
  3291.  
  3292.     my $r='';
  3293.  
  3294.      foreach $c (split(//,shift)){
  3295.  
  3296.         $r.=pack("v",ord($c));
  3297.  
  3298.     }
  3299.  
  3300.     return $r;
  3301.  
  3302. }
  3303.  
  3304.  
  3305.  
  3306. #########################################################################
  3307.  
  3308. =pod
  3309.  
  3310.  
  3311.  
  3312. =head1 ++ Sub package: forms
  3313.  
  3314.  
  3315.  
  3316. This subpackage contains various routines to parse and handle HTML forms.  
  3317.  
  3318. The goal is to parse the variable, human-readable HTML into concrete
  3319.  
  3320. structures useable by your program.  The forms package does do a good job
  3321.  
  3322. at making these structures, but I will admit: they are not exactly simple,
  3323.  
  3324. and thus not a cinch to work with.  But then again, representing something
  3325.  
  3326. as complex as a HTML form is not a simple thing either.  I think the
  3327.  
  3328. results are acceptable for what's trying to be done.  Anyways...
  3329.  
  3330.  
  3331.  
  3332. Forms are stored in perl hashes, with elements in the following format:
  3333.  
  3334.  
  3335.  
  3336.     $form{'element_name'}=@([ 'type', 'value', @params ])
  3337.  
  3338.  
  3339.  
  3340. Thus every element in the hash is an array of anonymous arrays.  The first
  3341.  
  3342. array value contains the element type (which is 'select', 'textarea',
  3343.  
  3344. 'button', or an 'input' value of the form 'input-text', 'input-hidden',
  3345.  
  3346. 'input-radio', etc).
  3347.  
  3348.  
  3349.  
  3350. The second value is the value, if applicable (it could be undef if no
  3351.  
  3352. value was specified).  Note that select elements will always have an undef
  3353.  
  3354. value--the actual values are in the subsequent options elements.
  3355.  
  3356.  
  3357.  
  3358. The third value, if defined, is an anonymous array of additional tag
  3359.  
  3360. parameters found in the element (like 'onchange="blah"', 'size="20"',
  3361.  
  3362. 'maxlength="40"', 'selected', etc).
  3363.  
  3364.  
  3365.  
  3366. The array does contain one special element, which is stored in the hash
  3367.  
  3368. under a NULL character ("\0") key.  This element is of the format:
  3369.  
  3370.  
  3371.  
  3372.     $form{"\0"}=['name', 'method', 'action', @parameters];
  3373.  
  3374.  
  3375.  
  3376. The element is an anonymous array that contains strings of the form's
  3377.  
  3378. name, method, and action (values can be undef), and a @parameters array
  3379.  
  3380. similar to that found in normal elements (above).
  3381.  
  3382.  
  3383.  
  3384. Accessing individual values stored in the form hash becomes a test of your
  3385.  
  3386. perl referencing skills.  Hint: to access the 'value' of the third element
  3387.  
  3388. named 'choices', you would need to do:
  3389.  
  3390.  
  3391.  
  3392.     $form{'choices'}->[2]->[1];
  3393.  
  3394.  
  3395.  
  3396. The '[2]' is the third element (normal array starts with 0), and the
  3397.  
  3398. actual value is '[1]' (the type is '[0]', and the parameter array is
  3399.  
  3400. '[2]').
  3401.  
  3402.  
  3403.  
  3404. =cut
  3405.  
  3406.  
  3407.  
  3408. ################################################################
  3409.  
  3410.  
  3411.  
  3412. =pod
  3413.  
  3414.  
  3415.  
  3416. =head1 - Function: LW::forms_read
  3417.  
  3418.   
  3419.  
  3420. Params: \$html_data
  3421.  
  3422. Return: @found_forms
  3423.  
  3424.  
  3425.  
  3426. This function parses the given $html_data into libwhisker form hashes.  
  3427.  
  3428. It returns an array of hash references to the found forms.
  3429.  
  3430.  
  3431.  
  3432. =cut
  3433.  
  3434.  
  3435.  
  3436. sub forms_read {
  3437.  
  3438.     my $dr=shift;
  3439.  
  3440.     return undef if(!ref($dr) || length($$dr)==0);
  3441.  
  3442.  
  3443.  
  3444.     @LW::forms_found=();
  3445.  
  3446.     LW::html_find_tags($dr,\&forms_parse_callback);
  3447.  
  3448.  
  3449.  
  3450.     if(scalar %LW::forms_current){
  3451.  
  3452.         my %DUP=%LW::forms_current;
  3453.  
  3454.         push(@LW::forms_found,\%DUP);
  3455.  
  3456.     }
  3457.  
  3458.     return @LW::forms_found;
  3459.  
  3460. }
  3461.  
  3462.  
  3463.  
  3464. ################################################################
  3465.  
  3466.  
  3467.  
  3468. =pod
  3469.  
  3470.  
  3471.  
  3472. =head1 - Function: LW::forms_write
  3473.  
  3474.   
  3475.  
  3476. Params: \%form_hash
  3477.  
  3478. Return: $html_of_form   [undef on error]
  3479.  
  3480.  
  3481.  
  3482. This function will take the given %form hash and compose a generic HTML
  3483.  
  3484. representation of it, formatted with tabs and newlines in order to make it
  3485.  
  3486. neat and tidy for printing.
  3487.  
  3488.  
  3489.  
  3490. Note: this function does *not* escape any special characters that were
  3491.  
  3492. embedded in the element values.
  3493.  
  3494.  
  3495.  
  3496. =cut
  3497.  
  3498.  
  3499.  
  3500. sub forms_write {
  3501.  
  3502.     my $hr=shift;
  3503.  
  3504.     return undef if(!ref($hr) || !(scalar %$hr));
  3505.  
  3506.     return undef if(!defined $$hr{"\0"});
  3507.  
  3508.     
  3509.  
  3510.     my $t='<form name="'.$$hr{"\0"}->[0].'" method="';
  3511.  
  3512.     $t.=$$hr{"\0"}->[1].'" action="'.$$hr{"\0"}->[2].'"';
  3513.  
  3514.     if(defined $$hr{"\0"}->[3]){
  3515.  
  3516.         $t.=' '.join(' ',@{$$hr{"\0"}->[3]}); }
  3517.  
  3518.     $t.=">\n";
  3519.  
  3520.  
  3521.  
  3522.     while( my($name,$ar)=each(%$hr) ){
  3523.  
  3524.       next if($name eq "\0");
  3525.  
  3526.       foreach $a (@$ar){
  3527.  
  3528.         my $P='';
  3529.  
  3530.         $P=' '.join(' ', @{$$a[2]}) if(defined $$a[2]);
  3531.  
  3532.         $t.="\t";
  3533.  
  3534.  
  3535.  
  3536.         if($$a[0] eq 'textarea'){
  3537.  
  3538.             $t.="<textarea name=\"$name\"$P>$$a[1]";
  3539.  
  3540.             $t.="</textarea>\n";
  3541.  
  3542.  
  3543.  
  3544.         } elsif($$a[0]=~m/^input-(.+)$/){
  3545.  
  3546.             $t.="<input type=\"$1\" name=\"$name\" ";
  3547.  
  3548.             $t.="value=\"$$a[1]\"$P>\n";
  3549.  
  3550.  
  3551.  
  3552.         } elsif($$a[0] eq 'option'){
  3553.  
  3554.             $t.="\t<option value=\"$$a[1]\"$P>$$a[1]\n";
  3555.  
  3556.  
  3557.  
  3558.         } elsif($$a[0] eq 'select'){
  3559.  
  3560.             $t.="<select name=\"$name\"$P>\n";
  3561.  
  3562.  
  3563.  
  3564.         } elsif($$a[0] eq '/select'){
  3565.  
  3566.             $t.="</select$P>\n";
  3567.  
  3568.  
  3569.  
  3570.         } else { # button
  3571.  
  3572.             $t.="<button name=\"$name\" value=\"$$a[1]\">\n";
  3573.  
  3574.         }
  3575.  
  3576.       }
  3577.  
  3578.     }
  3579.  
  3580.  
  3581.  
  3582.     $t.="</form>\n";
  3583.  
  3584.     return $t;
  3585.  
  3586. }
  3587.  
  3588.  
  3589.  
  3590. ################################################################
  3591.  
  3592.  
  3593.  
  3594.  
  3595.  
  3596. =pod
  3597.  
  3598.  
  3599.  
  3600. =head1 - Function: LW::forms_parse_html (INTERNAL)
  3601.  
  3602.   
  3603.  
  3604. Params: $TAG, \%elements, \$html_data, $offset, $len
  3605.  
  3606. Return: nothing
  3607.  
  3608.  
  3609.  
  3610. This is an &html_find_tags callback used to parse HTML into form hashes.  
  3611.  
  3612. You should not call this directly, but instead use &LW::forms_read.
  3613.  
  3614.  
  3615.  
  3616. =cut
  3617.  
  3618.  
  3619.  
  3620. { # these are private static variables for &forms_parse_html
  3621.  
  3622. %FORMS_ELEMENTS=(    'form'=>1,    'input'=>1,
  3623.  
  3624.             'textarea'=>1,    'button'=>1,
  3625.  
  3626.             'select'=>1,    'option'=>1,
  3627.  
  3628.             '/select'=>1    );
  3629.  
  3630. $CURRENT_SELECT=undef;
  3631.  
  3632. $UNKNOWNS=0;
  3633.  
  3634.  
  3635.  
  3636. sub forms_parse_callback {
  3637.  
  3638.     my ($TAG, $hr, $dr, $start, $len)=(lc(shift),@_);
  3639.  
  3640.     my ($saveparam, $parr, $key)=(0,undef,'');
  3641.  
  3642.  
  3643.  
  3644.     # fastpath shortcut
  3645.  
  3646.     return undef if(!defined $FORMS_ELEMENTS{$TAG});
  3647.  
  3648.     LW::utils_lowercase_hashkeys($hr) if(scalar %$hr);
  3649.  
  3650.  
  3651.  
  3652.     if($TAG eq 'form'){
  3653.  
  3654.  
  3655.  
  3656.         if(scalar %LW::forms_current){ # save last form
  3657.  
  3658.             my %DUP=%LW::forms_current;
  3659.  
  3660.             push (@LW::forms_found, \%DUP);
  3661.  
  3662.             %LW::forms_current=();
  3663.  
  3664.         }
  3665.  
  3666.  
  3667.  
  3668.         $LW::forms_current{"\0"}=[$$hr{name},$$hr{method},
  3669.  
  3670.             $$hr{action},undef];
  3671.  
  3672.         delete $$hr{'name'}; delete $$hr{'method'}; delete $$hr{'action'};
  3673.  
  3674.         $key="\0"; $parr=\@{$LW::forms_current{"\0"}};
  3675.  
  3676.         $UNKNOWNS=0;
  3677.  
  3678.  
  3679.  
  3680.     } elsif($TAG eq 'input'){
  3681.  
  3682.         $$hr{type}='text' if(!defined $$hr{type});
  3683.  
  3684.         $$hr{name}='unknown'.$UNKNOWNS++ if(!defined $$hr{name});
  3685.  
  3686.         $key=$$hr{name};
  3687.  
  3688.     
  3689.  
  3690.         push( @{$LW::forms_current{$key}}, 
  3691.  
  3692.             (['input-'.$$hr{type},$$hr{value},undef]) );
  3693.  
  3694.         delete $$hr{'name'}; delete $$hr{'type'}; delete $$hr{'value'};
  3695.  
  3696.         $parr=\@{$LW::forms_current{$key}->[-1]};
  3697.  
  3698.  
  3699.  
  3700.     } elsif($TAG eq 'select'){
  3701.  
  3702.         $$hr{name}='unknown'.$UNKNOWNS++ if(!defined $$hr{name});
  3703.  
  3704.         $key=$$hr{name};
  3705.  
  3706.         push( @{$LW::forms_current{$key}}, (['select',undef,undef]) );
  3707.  
  3708.         $parr=\@{$LW::forms_current{$key}->[-1]};
  3709.  
  3710.         $CURRENT_SELECT=$key;
  3711.  
  3712.         delete $$hr{name};
  3713.  
  3714.  
  3715.  
  3716.     } elsif($TAG eq '/select'){
  3717.  
  3718.         push( @{$LW::forms_current{$CURRENT_SELECT}}, 
  3719.  
  3720.             (['/select',undef,undef]) );
  3721.  
  3722.         $CURRENT_SELECT=undef;
  3723.  
  3724.         return undef;
  3725.  
  3726.  
  3727.  
  3728.     } elsif($TAG eq 'option'){
  3729.  
  3730.         return undef if(!defined $CURRENT_SELECT);
  3731.  
  3732.         if(!defined $$hr{value}){
  3733.  
  3734.             my $stop=index($$dr,'<',$start+$len);
  3735.  
  3736.             return undef if($stop==-1); # MAJOR PUKE
  3737.  
  3738.             $$hr{value}=substr($$dr,$start+$len,
  3739.  
  3740.                 ($stop-$start-$len));
  3741.  
  3742.             $$hr{value}=~tr/\r\n//d;
  3743.  
  3744.         }
  3745.  
  3746.         push( @{$LW::forms_current{$CURRENT_SELECT}}, 
  3747.  
  3748.             (['option',$$hr{value},undef]) );
  3749.  
  3750.         delete $$hr{value};
  3751.  
  3752.         $parr=\@{$LW::forms_current{$CURRENT_SELECT}->[-1]};
  3753.  
  3754.  
  3755.  
  3756.     } elsif($TAG eq 'textarea'){
  3757.  
  3758.         my $stop=$start+$len;
  3759.  
  3760.         # find closing </textarea> tag
  3761.  
  3762.         do {    $stop=index($$dr,'</',$stop+2); 
  3763.  
  3764.             return undef if($stop==-1); # MAJOR PUKE
  3765.  
  3766.         } while( lc(substr($$dr,$stop+2,8)) ne 'textarea');
  3767.  
  3768.         $$hr{value}=substr($$dr,$start+$len,($stop-$start-$len));
  3769.  
  3770.  
  3771.  
  3772.         $$hr{name}='unknown'.$UNKNOWNS++ if(!defined $$hr{name});
  3773.  
  3774.         $key=$$hr{name};
  3775.  
  3776.         push( @{$LW::forms_current{$key}}, 
  3777.  
  3778.             (['textarea',$$hr{value},undef]) );
  3779.  
  3780.         $parr=\@{$LW::forms_current{$key}->[-1]};
  3781.  
  3782.         delete $$hr{'name'}; delete $$hr{'value'};
  3783.  
  3784.  
  3785.  
  3786.     } else { # button
  3787.  
  3788.         $$hr{name}='unknown'.$UNKNOWNS++ if(!defined $$hr{name});
  3789.  
  3790.         $key=$$hr{name};
  3791.  
  3792.         push( @{$LW::forms_current{$key}}, 
  3793.  
  3794.             (['button',$$hr{value},undef]) );
  3795.  
  3796.     }
  3797.  
  3798.  
  3799.  
  3800.     if(scalar %$hr){
  3801.  
  3802.         my @params=();
  3803.  
  3804.         foreach $k (keys %$hr){
  3805.  
  3806.             if(defined $$hr{$k}){
  3807.  
  3808.                     push @params, "$k=\"$$hr{$k}\"";
  3809.  
  3810.             } else {    push @params, $k; }
  3811.  
  3812.         }
  3813.  
  3814.         $$parr[2]=\@params;
  3815.  
  3816.     }
  3817.  
  3818.  
  3819.  
  3820.     return undef;
  3821.  
  3822. }}
  3823.  
  3824.  
  3825.  
  3826. =pod
  3827.  
  3828.  
  3829.  
  3830. =head1 ++ Sub package: html
  3831.  
  3832.         
  3833.  
  3834. The html sub package implements a simple HTML parser.
  3835.  
  3836.  
  3837.  
  3838. =cut
  3839.  
  3840.  
  3841.  
  3842. ################################################################
  3843.  
  3844.  
  3845.  
  3846. =pod
  3847.  
  3848.  
  3849.  
  3850. =head1 - Function: LW::html_find_tags
  3851.  
  3852.   
  3853.  
  3854. Params: \$data, \&callback_function [, $xml_flag]
  3855.  
  3856. Return: nothing
  3857.  
  3858.  
  3859.  
  3860. LW::html_find_tags parses a piece of HTML and 'extracts' all found tags,
  3861.  
  3862. passing the info to the given callback function.  The callback function 
  3863.  
  3864. must accept two parameters: the current tag (as a scalar), and a hash ref 
  3865.  
  3866. of all the tag's elements. For example, the tag <a href="/file"> will
  3867.  
  3868. pass 'a' as the current tag, and a hash reference which contains
  3869.  
  3870. {'href'}="/file".
  3871.  
  3872.  
  3873.  
  3874. The xml_flag, when set, causes the parser to do some extra processing
  3875.  
  3876. and checks to accomodate XML style tags such as <tag foo="bar"/>.
  3877.  
  3878.  
  3879.  
  3880. Notice: this function is slow! And using it to rewrite (via passback) is 
  3881.  
  3882. slower!  Make sure you have LW::bin installed to get the faster binary 
  3883.  
  3884. version.
  3885.  
  3886.  
  3887.  
  3888. =cut
  3889.  
  3890.  
  3891.  
  3892. sub html_find_tags {
  3893.  
  3894.  # use faster binary helper
  3895.  
  3896.  goto &LW::bin::html_find_tags 
  3897.  
  3898.      if(defined $LW::available{'lw::bin'});
  3899.  
  3900.     
  3901.  
  3902.  my ($dataref, $callbackfunc, $xml)=@_;
  3903.  
  3904.  
  3905.  
  3906.  return if(!(defined $dataref      && ref($dataref)     ));
  3907.  
  3908.  return if(!(defined $callbackfunc && ref($callbackfunc)));
  3909.  
  3910.  $xml||=0;
  3911.  
  3912.  
  3913.  
  3914.  my ($CURTAG, $ELEMENT, $VALUE, $c, $cc);
  3915.  
  3916.  my ($INCOMMENT,$INTAG,$INSCRIPT,$INCDATA)=(0,0,0,0);
  3917.  
  3918.  my (%TAG, $ret, $start, $tagstart, $commstart, $scriptstart, $x);
  3919.  
  3920.  
  3921.  
  3922.  # YES, this looks like C.  In fact, it's my C version ported to
  3923.  
  3924.  # perl.  But it's faster and more dependable than any regex mess
  3925.  
  3926.  # someone could come up with.
  3927.  
  3928.  my $LEN = length($$dataref);
  3929.  
  3930.  for ($c=0; $c<$LEN; $c++){
  3931.  
  3932.  
  3933.  
  3934.     $cc=substr($$dataref,$c,1);
  3935.  
  3936.  
  3937.  
  3938.     next if(!$INCOMMENT && !$INTAG && !$INSCRIPT && $cc ne '>' && $cc ne '<');
  3939.  
  3940.  
  3941.  
  3942.         if($cc eq '<'){
  3943.  
  3944.         if($INSCRIPT){
  3945.  
  3946.             if(lc(substr($$dataref,$c+1,7)) eq '/script'){
  3947.  
  3948.                 $INSCRIPT=0;
  3949.  
  3950.                 $TAG{'='}=substr($$dataref, $scriptstart,
  3951.  
  3952.                     $c - $scriptstart - 1);
  3953.  
  3954.             } else { next; }
  3955.  
  3956.         }
  3957.  
  3958.  
  3959.  
  3960.         next if($INCDATA); # skip tags in xml CDATA section
  3961.  
  3962.  
  3963.  
  3964.                 if(substr($$dataref,$c+1,3) eq '!--'){
  3965.  
  3966.                         $INCOMMENT=1; $commstart=$c; $c+=3;
  3967.  
  3968.  
  3969.  
  3970.         $INCDATA++ if($xml&&substr($$dataref,$c+1,8) eq '![CDATA[');
  3971.  
  3972.  
  3973.  
  3974.         } else {
  3975.  
  3976.                         $INTAG=1; $c++;
  3977.  
  3978.             $c++ while(substr($$dataref,$c,1)=~tr/< \t\r\n//);
  3979.  
  3980.             $tagstart=$c-1; 
  3981.  
  3982.  
  3983.  
  3984.             $CURTAG='';
  3985.  
  3986.             while(($x=substr($$dataref,$c,1))!~tr/ \t\r\n>=// &&
  3987.  
  3988.                     $c < $LEN){
  3989.  
  3990.                 $CURTAG.=$x; $c++;}
  3991.  
  3992.  
  3993.  
  3994.             chop $CURTAG if($xml && substr($CURTAG,-1,1) eq '/');
  3995.  
  3996.             $c++ if($x ne '>');
  3997.  
  3998.  
  3999.  
  4000.             $INSCRIPT=1 if($CURTAG eq 'script' && !$xml);
  4001.  
  4002.         }    
  4003.  
  4004.         $cc=substr($$dataref,$c,1); # refresh current char (cc)
  4005.  
  4006.     }
  4007.  
  4008.  
  4009.  
  4010.         if($cc eq '>'){
  4011.  
  4012.         if($INSCRIPT){
  4013.  
  4014.             if($CURTAG eq 'script'){
  4015.  
  4016.                 $scriptstart = $c + 1; 
  4017.  
  4018.             } else { next; }
  4019.  
  4020.         }
  4021.  
  4022.  
  4023.  
  4024.         if($INCDATA && substr($$dataref,$c-2,2) eq ']]'){
  4025.  
  4026.             $INCDATA=0;
  4027.  
  4028.             next;
  4029.  
  4030.         }
  4031.  
  4032.  
  4033.  
  4034.         if(!$INCOMMENT && $INTAG){ 
  4035.  
  4036.             $INTAG=0; 
  4037.  
  4038.             $TAG{'/'}++ if($xml&&substr($$dataref,$c-1,1) eq '/');
  4039.  
  4040.             $ret=&$callbackfunc($CURTAG,\%TAG, $dataref,
  4041.  
  4042.                 $tagstart, $c-$tagstart+1);
  4043.  
  4044.             $c+=$ret if(defined $ret && $ret != 0);
  4045.  
  4046.             $CURTAG='';
  4047.  
  4048.             %TAG=();
  4049.  
  4050.         }
  4051.  
  4052.                 if($INCOMMENT && substr($$dataref,$c-2,2) eq '--'){
  4053.  
  4054.                         $INCOMMENT=0; 
  4055.  
  4056.             $TAG{'='}=substr($$dataref,$commstart+4,
  4057.  
  4058.                 $c-$commstart-3);
  4059.  
  4060.             $ret=&$callbackfunc('!--',\%TAG, $dataref,
  4061.  
  4062.                 $commstart, $c-$commstart+1);
  4063.  
  4064.             $c+=$ret if(defined $ret && $ret != 0);
  4065.  
  4066.             delete $TAG{'='};
  4067.  
  4068.             next;
  4069.  
  4070.         }
  4071.  
  4072.     }
  4073.  
  4074.  
  4075.  
  4076.         next if($INCOMMENT);
  4077.  
  4078.  
  4079.  
  4080.         if($INTAG){
  4081.  
  4082.  
  4083.  
  4084.                 $ELEMENT=''; $VALUE='';
  4085.  
  4086.  
  4087.  
  4088.         # eat whitespace
  4089.  
  4090.         $c++ while(substr($$dataref,$c,1)=~tr/ \t\r\n//);
  4091.  
  4092.  
  4093.  
  4094.         $start=$c;
  4095.  
  4096.         $c++ while(substr($$dataref,$c,1)!~tr/ \t\r\n=\>// && $c<$LEN);
  4097.  
  4098.  
  4099.  
  4100.         if($c > $start){
  4101.  
  4102.             $ELEMENT=substr($$dataref,$start,$c-$start);
  4103.  
  4104.             chop $ELEMENT if($xml&&substr($ELEMENT,-1,1) eq '/');
  4105.  
  4106.         }
  4107.  
  4108.  
  4109.  
  4110.         if(substr($$dataref,$c,1) ne '>'){
  4111.  
  4112.          # eat whitespace
  4113.  
  4114.          $c++ while(substr($$dataref,$c,1)=~tr/ \t\r\n//);
  4115.  
  4116.  
  4117.  
  4118.                  if(substr($$dataref,$c,1) eq '='){ 
  4119.  
  4120.                     $c++;
  4121.  
  4122.             $start=$c;
  4123.  
  4124.             my $p = substr($$dataref,$c,1);
  4125.  
  4126.                         if($p eq '"' || $p eq '\''){ 
  4127.  
  4128.                             $c++; $start++;
  4129.  
  4130.                             $c++ while(substr($$dataref,$c,1) ne $p &&
  4131.  
  4132.                                 $c < $LEN);
  4133.  
  4134.                 $VALUE=substr($$dataref,$start,$c-$start);
  4135.  
  4136.                                 $c++; 
  4137.  
  4138.             } else {
  4139.  
  4140.                                 $c++ while(substr($$dataref,$c,1)!~tr/ \t\r\n\>// &&
  4141.  
  4142.                                     $c < $LEN);
  4143.  
  4144.                 $VALUE=substr($$dataref,$start,$c-$start);
  4145.  
  4146.                 chop $VALUE if($xml&&substr($$dataref,$c-1,2) eq '/>');
  4147.  
  4148.             }
  4149.  
  4150.  
  4151.  
  4152.             # eat whitespace
  4153.  
  4154.                     $c++ while(substr($$dataref,$c,1)=~tr/ \t\r\n//);
  4155.  
  4156.                  } 
  4157.  
  4158.         } # if $c ne '>'
  4159.  
  4160.         $c--;
  4161.  
  4162.         $TAG{$ELEMENT}=$VALUE if($ELEMENT ne '' && ($xml && $ELEMENT ne '/'));
  4163.  
  4164.     }
  4165.  
  4166. }}
  4167.  
  4168.  
  4169.  
  4170. ################################################################
  4171.  
  4172.  
  4173.  
  4174. =pod
  4175.  
  4176.  
  4177.  
  4178. =head1 ++ Sub package: http
  4179.  
  4180.  
  4181.  
  4182. The http package is the core package of libwhisker.  It is responsible
  4183.  
  4184. for making the HTTP requests, and parsing the responses.  It can handle
  4185.  
  4186. HTTP 0.9, 1.0, and 1.1 requests, and allows pretty much every aspect of
  4187.  
  4188. the request to be configured and controlled.  The HTTP functions use a
  4189.  
  4190. HTTP in/out hash, which is a normal perl hash.  For outgoing HTTP requests
  4191.  
  4192. ('hin' hashes), the keys/values represent outgoing HTTP headers.  For HTTP
  4193.  
  4194. responses ('hout' hashes), the keys/values represent incoming HTTP
  4195.  
  4196. headers.  For both, however, there is a special key, 'whisker', whose
  4197.  
  4198. value is a hash reference.  The whisker control hash contains more
  4199.  
  4200. configuration variables, which include host, port, and uri of the desired
  4201.  
  4202. request.  To access the whisker control hash, use the following
  4203.  
  4204. notation: $hash{'whisker'}->{'key'}='value';
  4205.  
  4206.  
  4207.  
  4208. You should view LW::http_init_request() for a list of core whisker control
  4209.  
  4210. hash values.
  4211.  
  4212.  
  4213.  
  4214. The internals of the http subpackage will be rewritten shortly--the 
  4215.  
  4216. current implementation is gross and not very good style.  Note that the
  4217.  
  4218. API will be unaffected; it will only be an internal reordering.  All
  4219.  
  4220. references/uses of $$Z will be cleaned up to be more practical/eliminated.
  4221.  
  4222.  
  4223.  
  4224. =cut
  4225.  
  4226.  
  4227.  
  4228. ##################################################################
  4229.  
  4230.  
  4231.  
  4232. =pod
  4233.  
  4234.  
  4235.  
  4236. =head1 - Function: LW::http_init_request
  4237.  
  4238.    
  4239.  
  4240. Params: \%request_hash_to_initialize
  4241.  
  4242. Return: Nothing (modifies input hash)
  4243.  
  4244.  
  4245.  
  4246. Sets default values to the input hash for use.  Sets the host to
  4247.  
  4248. 'localhost', port 80, request URI '/', using HTTP 1.1 with GET
  4249.  
  4250. method.  The timeout is set to 10 seconds, no proxies are defined, and all
  4251.  
  4252. URI formatting is set to standard HTTP syntax.  It also sets the
  4253.  
  4254. Connection (Keep-Alive) and User-Agent headers.
  4255.  
  4256.  
  4257.  
  4258. NOTICE!!  It's important to use http_init_request before calling 
  4259.  
  4260. http_do_request, or http_do_request might puke.  Thus, a special magic 
  4261.  
  4262. value is placed in the hash to let http_do_request know that the hash has 
  4263.  
  4264. been properly initialized.  If you really must 'roll your own' and not use 
  4265.  
  4266. http_init_request before you call http_do_request, you will at least need 
  4267.  
  4268. to set the INITIAL_MAGIC value (amongst other things).
  4269.  
  4270.  
  4271.  
  4272. =cut
  4273.  
  4274.  
  4275.  
  4276. sub http_init_request { # doesn't return anything
  4277.  
  4278.  my ($hin)=shift;
  4279.  
  4280.  
  4281.  
  4282.  return if(!(defined $hin && ref($hin)));
  4283.  
  4284.  %$hin=(); # clear control hash
  4285.  
  4286.  
  4287.  
  4288. # control values
  4289.  
  4290.  $$hin{'whisker'}={
  4291.  
  4292.     req_spacer        =>    ' ',
  4293.  
  4294.     req_spacer2        =>    ' ',
  4295.  
  4296.     http_ver        =>    '1.1',
  4297.  
  4298.     method            =>    'GET',
  4299.  
  4300.     method_postfix        =>    '',
  4301.  
  4302.     port            =>    80,
  4303.  
  4304.     uri            =>    '/',
  4305.  
  4306.     uri_prefix        =>    '',
  4307.  
  4308.     uri_postfix        =>    '',
  4309.  
  4310.     uri_param_sep        =>    '?',
  4311.  
  4312.     host            =>    'localhost',
  4313.  
  4314.     http_req_trailer        =>    '',
  4315.  
  4316.     timeout            =>    10,
  4317.  
  4318.     include_host_in_uri     =>    0,
  4319.  
  4320.     ignore_duplicate_headers=>     1,
  4321.  
  4322.     normalize_incoming_headers =>    1,
  4323.  
  4324.     lowercase_incoming_headers =>    0,
  4325.  
  4326.     ssl            =>    0,
  4327.  
  4328.     http_eol        =>    "\x0d\x0a",
  4329.  
  4330.     force_close        =>    0,
  4331.  
  4332.     force_open        =>    0,
  4333.  
  4334.     retry            =>    1,
  4335.  
  4336.     trailing_slurp        =>    0,
  4337.  
  4338.     force_bodysnatch    =>    0,
  4339.  
  4340.     INITIAL_MAGIC        =>    31337
  4341.  
  4342. };
  4343.  
  4344.  
  4345.  
  4346.  
  4347.  
  4348. # default header values
  4349.  
  4350.  $$hin{'Connection'}='Keep-Alive'; # notice it is now default!
  4351.  
  4352.  $$hin{'User-Agent'}="libwhisker/$LW::VERSION"; # heh
  4353.  
  4354. }
  4355.  
  4356.  
  4357.  
  4358.  
  4359.  
  4360. ##################################################################
  4361.  
  4362.  
  4363.  
  4364. =pod
  4365.  
  4366.  
  4367.  
  4368. =head1 - Function: LW::http_do_request
  4369.  
  4370.    
  4371.  
  4372. Params: \%request, \%response [, \%configs]
  4373.  
  4374. Return: >=1 if error; 0 if no error (also modifies response hash)
  4375.  
  4376.  
  4377.  
  4378. *THE* core function of libwhisker.  LW::http_do_request actually performs
  4379.  
  4380. the HTTP request, using the values submitted in %request, and placing result
  4381.  
  4382. values in %response.  This allows you to resubmit %request in subsequent 
  4383.  
  4384. requests (%response is automatically cleared upon execution).  You can 
  4385.  
  4386. submit 'runtime' config directives as %configs, which will be spliced into
  4387.  
  4388. $hin{'whisker'}->{} before anything else.  That means you can do:
  4389.  
  4390.  
  4391.  
  4392. LW::http_do_request(\%req,\%resp,{'uri'=>'/cgi-bin/'});
  4393.  
  4394.  
  4395.  
  4396. This will set $req{'whisker'}->{'uri'}='/cgi-bin/' before execution, and
  4397.  
  4398. provides a simple shortcut (note: it does modify %req).
  4399.  
  4400.  
  4401.  
  4402. This function will also retry any requests that bomb out during the 
  4403.  
  4404. transaction (but not during the connecting phase).  This is controlled
  4405.  
  4406. by the {whisker}->{retry} value.  Also note that the returned error
  4407.  
  4408. message in resp is the *last* error received.  All retry errors are
  4409.  
  4410. put into {whisker}->{retry_errors}, which is an anonymous array.
  4411.  
  4412.  
  4413.  
  4414. Also note that all NTLM auth logic is implemented in http_do_request().
  4415.  
  4416. NTLM requires multiple requests in order to work correctly, and so this
  4417.  
  4418. function attempts to wrap that and make it all transparent, so that the
  4419.  
  4420. final end result is what's passed to the application.
  4421.  
  4422.  
  4423.  
  4424. This function will return 0 on success, 1 on HTTP protocol error, and 2
  4425.  
  4426. on non-recoverable network connection error (you can retry error 1, but
  4427.  
  4428. error 2 means that the server is totally unreachable and there's no
  4429.  
  4430. point in retrying).
  4431.  
  4432.  
  4433.  
  4434. =cut
  4435.  
  4436.  
  4437.  
  4438. sub http_do_request {
  4439.  
  4440.  my @params = @_;
  4441.  
  4442.  my $retry_count = ${$params[0]}{'whisker'}->{'retry'} || 0;
  4443.  
  4444.  my ($ret, @retry_errors, $auth);
  4445.  
  4446.  
  4447.  
  4448.  return 1 if(!(defined $params[0] && ref($params[0])));
  4449.  
  4450.  return 1 if(!(defined $params[1] && ref($params[1])));
  4451.  
  4452.  
  4453.  
  4454.  if(defined $params[2]){
  4455.  
  4456.     foreach (keys %{$params[2]}){
  4457.  
  4458.         ${$params[0]}{'whisker'}->{$_}=${$params[2]}{$_};}}
  4459.  
  4460.  
  4461.  
  4462.  $auth=$params[0]->{'Authorization'} if(defined $params[0]->{'Authorization'});
  4463.  
  4464.  do {
  4465.  
  4466.     if(defined $auth && $auth=~/^NTLM/){
  4467.  
  4468.     $ret=0;
  4469.  
  4470.     if($params[0]->{'whisker'}->{'ntlm_step'}==0){
  4471.  
  4472.         $ret=LW::http_do_request_ex($params[0],$params[1]);
  4473.  
  4474.         return 2 if($ret==2);
  4475.  
  4476.         if($ret==0){
  4477.  
  4478.             return 0 if($params[1]->{'whisker'}->{'code'} == 200);
  4479.  
  4480.             return 1 if($params[1]->{'whisker'}->{'code'} != 401);
  4481.  
  4482.             $params[0]->{'whisker'}->{'ntlm_step'}=1;
  4483.  
  4484.             my $thead=utils_find_lowercase_key($params[1],'www-authenticate');
  4485.  
  4486.             return 1 if(!defined $thead);
  4487.  
  4488.             return 1 if($thead!~m/^NTLM (.+)$/);  
  4489.  
  4490.             $params[0]->{'Authorization'}='NTLM '.ntlm_client(
  4491.  
  4492.                 $params[0]->{'whisker'}->{'ntlm_obj'},$1);
  4493.  
  4494.         }
  4495.  
  4496.     }
  4497.  
  4498.     if($ret==0){
  4499.  
  4500.         delete $params[0]->{'Authorization'}
  4501.  
  4502.             if($params[0]->{'whisker'}->{'ntlm_step'}>1);
  4503.  
  4504.         $ret=LW::http_do_request_ex($params[0],$params[1]);
  4505.  
  4506.         $params[0]->{'Authorization'}=$auth; 
  4507.  
  4508.         if($ret>0){     $params[0]->{'whisker'}->{'ntlm_step'}=0;
  4509.  
  4510.         } else {    $params[0]->{'whisker'}->{'ntlm_step'}=2; }
  4511.  
  4512.         return $ret if($ret==2||$ret==0);
  4513.  
  4514.     }
  4515.  
  4516.     } else {
  4517.  
  4518.         $ret=LW::http_do_request_ex($params[0],$params[1]);
  4519.  
  4520.     push @{${$params[1]}{'whisker'}->{'retry_errors'}},
  4521.  
  4522.         @retry_errors if scalar(@retry_errors);
  4523.  
  4524.     return $ret if($ret==0 || $ret==2);
  4525.  
  4526.     }
  4527.  
  4528.     push @retry_errors, ${$params[1]}{'whisker'}->{'error'};
  4529.  
  4530.     $retry_count--;
  4531.  
  4532.   } while( $retry_count >= 0);
  4533.  
  4534.  
  4535.  
  4536.  # if we get here, we still had errors, but no more retries
  4537.  
  4538.  return 1;
  4539.  
  4540. }
  4541.  
  4542.  
  4543.  
  4544. ##################################################################
  4545.  
  4546.  
  4547.  
  4548. =pod
  4549.  
  4550.  
  4551.  
  4552. =head1 - Function: LW::http_do_request_ex
  4553.  
  4554.    
  4555.  
  4556. Params: \%req, \%resp, \%configs
  4557.  
  4558. Return: >=1 if error; 0 if no error
  4559.  
  4560.  
  4561.  
  4562. NOTE: you should go through http_do_request(), which calls this function.
  4563.  
  4564.  
  4565.  
  4566. This function actually does all the request work.  It is called by
  4567.  
  4568. http_do_request(), which has a 'retry wrapper' built into it to catch
  4569.  
  4570. errors.
  4571.  
  4572.  
  4573.  
  4574. =cut
  4575.  
  4576.  
  4577.  
  4578. sub http_do_request_ex {
  4579.  
  4580.  my ($hin, $hout, $hashref)=@_;
  4581.  
  4582.  my ($temp,$vin,$resp,$S,$a,$b,$vout,@c,$c,$res)=(1,'');
  4583.  
  4584.  my $W; # shorthand alias for the {'whisker'} hash
  4585.  
  4586.  
  4587.  
  4588.  return 1 if(!(defined $hin  && ref($hin) ));
  4589.  
  4590.  return 1 if(!(defined $hout && ref($hout)));
  4591.  
  4592.  
  4593.  
  4594.  %$hout=(); # clear output hash
  4595.  
  4596.  $$hout{whisker}->{uri}=$$hin{whisker}->{uri}; # for tracking purposes
  4597.  
  4598.  $$hout{whisker}->{'INITIAL_MAGIC'}=31338; # we can tell requests from responses
  4599.  
  4600.  
  4601.  
  4602.  if($LW::LW_HAS_SOCKET==0){
  4603.  
  4604.     $$hout{'whisker'}->{'error'}='Socket support not available';
  4605.  
  4606.     return 2;}
  4607.  
  4608.  
  4609.  
  4610.  if(!defined $$hin{'whisker'} || 
  4611.  
  4612.     !defined $$hin{'whisker'}->{'INITIAL_MAGIC'} ||
  4613.  
  4614.     $$hin{'whisker'}->{'INITIAL_MAGIC'}!=31337 ){
  4615.  
  4616.     $$hout{'whisker'}->{'error'}='Input hash not initialized';
  4617.  
  4618.     return 2;
  4619.  
  4620.  }
  4621.  
  4622.  
  4623.  
  4624.  if(defined $hashref){
  4625.  
  4626.     foreach (keys %$hashref){
  4627.  
  4628.         $$hin{'whisker'}->{$_}=$$hashref{$_};}}
  4629.  
  4630.  
  4631.  
  4632.  # if we want anti-IDS, make a copy and setup new values
  4633.  
  4634.  if(defined $$hin{'whisker'}->{'anti_ids'}){
  4635.  
  4636.     my %copy=%{$hin};
  4637.  
  4638.     anti_ids(\%copy,$$hin{'whisker'}->{'anti_ids'});
  4639.  
  4640.     $W = $copy{'whisker'};
  4641.  
  4642.  } else {
  4643.  
  4644.     $W = $$hin{'whisker'};
  4645.  
  4646.  }
  4647.  
  4648.  
  4649.  
  4650.  if($$W{'ssl'}>0 && $LW::LW_HAS_SSL!=1){
  4651.  
  4652.     $$hout{'whisker'}->{'error'}='SSL not available';
  4653.  
  4654.     return 2;}
  4655.  
  4656.  
  4657.  
  4658.  $TIMEOUT=$$W{'timeout'}||10;
  4659.  
  4660.  
  4661.  
  4662.  my $cache_key = defined $$W{'proxy_host'} ?
  4663.  
  4664.     join(':',$$W{'proxy_host'},$$W{'proxy_port'}) :
  4665.  
  4666.     join(':',$$W{'host'},$$W{'port'});
  4667.  
  4668.  
  4669.  
  4670.  if(!defined $http_host_cache{$cache_key}){
  4671.  
  4672.     # make new entry
  4673.  
  4674.     push(@{$http_host_cache{$cache_key}},
  4675.  
  4676.         undef,     # SOCKET        $$Z[0]
  4677.  
  4678.         0,    # $SOCKSTATE        $$Z[1]
  4679.  
  4680.         undef,    # INET_ATON        $$Z[2]
  4681.  
  4682.         undef,    # $SSL_CTX        $$Z[3]
  4683.  
  4684.         undef,    # $SSL_THINGY        $$Z[4]
  4685.  
  4686.         '',    # $OUTGOING_QUEUE    $$Z[5]
  4687.  
  4688.         '',    # $INCOMING_QUEUE    $$Z[6]
  4689.  
  4690.         0,    # $STATS_SYNS        $$Z[7]
  4691.  
  4692.         0,     # $STATS_REQS        $$Z[8]
  4693.  
  4694.         undef ) # SSL session ID    $$Z[9]
  4695.  
  4696.  }
  4697.  
  4698.  
  4699.  
  4700.  # NOTE: the 'Z' reference will be going away in future versions...
  4701.  
  4702.  $Z = $http_host_cache{$cache_key};
  4703.  
  4704.  
  4705.  
  4706.  # use $chost/$cport for actual server we are connecting to
  4707.  
  4708.  my ($chost,$cport,$cwhat,$PROXY)=('',80,'',0);
  4709.  
  4710.  
  4711.  
  4712.  if(defined $$W{'proxy_host'}){
  4713.  
  4714.     $chost=$$W{'proxy_host'};
  4715.  
  4716.     $cport=$$W{'proxy_port'}||80;
  4717.  
  4718.     $cwhat='proxy';
  4719.  
  4720.     $PROXY=1;
  4721.  
  4722.  
  4723.  
  4724.     if($$W{'ssl'}>0 && $LW::LW_SSL_LIB==2){
  4725.  
  4726.     $ENV{HTTPS_PROXY} ="$$W{'proxy_host'}:";
  4727.  
  4728.     $ENV{HTTPS_PROXY}.=$$W{'proxy_port'}||80; }
  4729.  
  4730.  
  4731.  
  4732.  } else {
  4733.  
  4734.     $chost=$$W{'host'};
  4735.  
  4736.     $cport=$$W{'port'};
  4737.  
  4738.     $cwhat='host';
  4739.  
  4740.  }
  4741.  
  4742.  
  4743.  
  4744.  if($$Z[1]>0){ # check to see if socket is still alive
  4745.  
  4746.     if(! sock_valid($Z,$hin,$hout) ){
  4747.  
  4748.         $$Z[1]=0;
  4749.  
  4750.         sock_close($$Z[0],$$Z[4]);
  4751.  
  4752.  }    }
  4753.  
  4754.  # technically we have a race condition: socket can go
  4755.  
  4756.  # bad before we send request, below.  But that's ok,
  4757.  
  4758.  # we handle the errors down there.
  4759.  
  4760.  
  4761.  
  4762.  if($$Z[1]==0){
  4763.  
  4764.  
  4765.  
  4766.     my $SOCK = _newsym();     
  4767.  
  4768.     if(defined $$W{'UDP'} && $$W{'UDP'}>0){
  4769.  
  4770.         if(!socket($SOCK,PF_INET,SOCK_DGRAM,getprotobyname('udp')||0)){
  4771.  
  4772.             $$hout{'whisker'}->{'error'}='Socket() problems (UDP)'; 
  4773.  
  4774.             return 2;}
  4775.  
  4776.     } else {
  4777.  
  4778.         if(!socket($SOCK,PF_INET,SOCK_STREAM,getprotobyname('tcp')||0)){
  4779.  
  4780.             $$hout{'whisker'}->{'error'}='Socket() problems'; 
  4781.  
  4782.             return 2;}
  4783.  
  4784.     }
  4785.  
  4786.  
  4787.  
  4788.     $$Z[0]=$SOCK;
  4789.  
  4790.  
  4791.  
  4792.     if(defined $$W{'bind_socket'}){
  4793.  
  4794.         my $port=$$W{'bind_port'}||14011;
  4795.  
  4796.         my $addr;
  4797.  
  4798.         if(defined $$W{'bind_addr'}){
  4799.  
  4800.             $addr=inet_aton($$W{'bind_addr'});
  4801.  
  4802.         } else {
  4803.  
  4804.             $addr=INADDR_ANY;
  4805.  
  4806.         }
  4807.  
  4808.         if(!bind($SOCK, sockaddr_in($port,$addr))){
  4809.  
  4810.             $$hout{'whisker'}->{'error'}='Bind() on socket failed';
  4811.  
  4812.             return 2;
  4813.  
  4814.         }
  4815.  
  4816.     }
  4817.  
  4818.  
  4819.  
  4820.     $$Z[5]=$$Z[6]=''; # flush in/out queues
  4821.  
  4822.  
  4823.  
  4824.     if($$W{'ssl'}>0){ # ssl setup stuff
  4825.  
  4826.  
  4827.  
  4828.         if($LW::LW_SSL_LIB==1){
  4829.  
  4830.         if(!defined($$Z[3])){
  4831.  
  4832.             if(! ($$Z[3] = Net::SSLeay::CTX_new()) ){
  4833.  
  4834.             $$hout{'whisker'}->{'error'}="SSL_CTX error: $!";
  4835.  
  4836.             return 2;}
  4837.  
  4838.             if(defined $$W{'ssl_rsacertfile'}){
  4839.  
  4840.             if(! (Net::SSLeay::CTX_use_RSAPrivateKey_file($$Z[3], 
  4841.  
  4842.                     $$W{'ssl_rsacertfile'},
  4843.  
  4844.                     &Net::SSLeay::FILETYPE_PEM))){
  4845.  
  4846.                 $$hout{'whisker'}->{'error'}="SSL_CTX_use_rsacert error: $!";
  4847.  
  4848.                 return 2;}
  4849.  
  4850.             }
  4851.  
  4852.             if(defined $$W{'ssl_certfile'}){
  4853.  
  4854.             if(! (Net::SSLeay::CTX_use_certificate_file($$Z[3], 
  4855.  
  4856.                     $$W{'ssl_certfile'},
  4857.  
  4858.                     &Net::SSLeay::FILETYPE_PEM))){
  4859.  
  4860.                 $$hout{'whisker'}->{'error'}="SSL_CTX_use_cert error: $!";
  4861.  
  4862.                 return 2;}
  4863.  
  4864.             }
  4865.  
  4866.         }
  4867.  
  4868.         if(! ($$Z[4] = Net::SSLeay::new($$Z[3])) ){
  4869.  
  4870.             $$hout{'whisker'}->{'error'}="SSL_new error: $!";
  4871.  
  4872.             return 2;}
  4873.  
  4874.         if(defined $$W{'ssl_ciphers'}){
  4875.  
  4876.             if(!(Net::SSLeay::set_cipher_list($$Z[4], 
  4877.  
  4878.                     $$W{'ssl_ciphers'}))){
  4879.  
  4880.                 $$hout{'whisker'}->{'error'}="SSL_set_ciphers error: $!";
  4881.  
  4882.                 return 2;}
  4883.  
  4884.         }
  4885.  
  4886.         }
  4887.  
  4888.     }
  4889.  
  4890.  
  4891.  
  4892.     $$Z[2]=inet_aton($chost) if(!defined $$Z[2]);
  4893.  
  4894.     if(!defined $$Z[2]){ # can't find hostname
  4895.  
  4896.         $$hout{'whisker'}->{'error'}="Can't resolve hostname";
  4897.  
  4898.         return 2;
  4899.  
  4900.     }
  4901.  
  4902.  
  4903.  
  4904.     if($$W{'ssl'}>0 && $LW::LW_SSL_LIB==2){
  4905.  
  4906.         # proxy set in ENV; we always connect to host
  4907.  
  4908.         $$Z[4]= Net::SSL->new(
  4909.  
  4910.             PeerAddr => $$hin{'whisker'}->{'host'},
  4911.  
  4912.             PeerPort => $$hin{'whisker'}->{'port'},
  4913.  
  4914.             Timeout => $TIMEOUT );
  4915.  
  4916.         if($@){ $$hout{'whisker'}->{'error'}="Can't connect via SSL: $@[0]";
  4917.  
  4918.             return 2;}
  4919.  
  4920.         $$Z[4]->autoflush(1);
  4921.  
  4922.     } else {
  4923.  
  4924.         if($LW::LW_NONBLOCK_CONNECT){
  4925.  
  4926.             my $flags=fcntl($$Z[0],F_GETFL,0);
  4927.  
  4928.             $flags |= O_NONBLOCK; # set nonblock flag
  4929.  
  4930.             if(!(fcntl($$Z[0],F_SETFL,$flags))){ # error setting flag
  4931.  
  4932.                 $LW::LW_NONBLOCK_CONNECT=0; # revert to normal
  4933.  
  4934.             } else {
  4935.  
  4936.                 my $R=connect($$Z[0],sockaddr_in($cport,$$Z[2]));
  4937.  
  4938.                 if(!$R){ # we didn't connect...
  4939.  
  4940.                     if($! != EINPROGRESS){
  4941.  
  4942.                         close($$Z[0]);
  4943.  
  4944.                         $$Z[0]=undef; # this is a bad socket
  4945.  
  4946.                         $$hout{'whisker'}->{'error'}="Can't connect to $cwhat";
  4947.  
  4948.                         return 2;}
  4949.  
  4950.                     vec($vin,fileno($$Z[0]),1)=1;
  4951.  
  4952.                     if(!select(undef,$vin,undef,$TIMEOUT) || !getpeername($$Z[0])){
  4953.  
  4954.                         close($$Z[0]);
  4955.  
  4956.                         $$Z[0]=undef; # this is a bad socket
  4957.  
  4958.                         $$hout{'whisker'}->{'error'}="Can't connect to $cwhat";
  4959.  
  4960.                         return 2;
  4961.  
  4962.                 }    }
  4963.  
  4964.                 $flags &= ~O_NONBLOCK; # clear nonblock flag
  4965.  
  4966.                 if(!(fcntl($$Z[0],F_SETFL,$flags))){ # not good!
  4967.  
  4968.                     close($$Z[0]);
  4969.  
  4970.                     $LW::LW_NONBLOCK_CONNECT=0;
  4971.  
  4972.                     $$Z[0]=undef;
  4973.  
  4974.                     $$hout{'whisker'}->{'error'}="Error setting socket to block";
  4975.  
  4976.                     return 2;
  4977.  
  4978.             }    }    
  4979.  
  4980.         }    
  4981.  
  4982.  
  4983.  
  4984.         if(!defined $$Z[0]){ # this is a safety catch
  4985.  
  4986.             $$hout{'whisker'}->{'error'}="Error creating valid socket connection";
  4987.  
  4988.             return 2; }
  4989.  
  4990.  
  4991.  
  4992.         if($LW::LW_NONBLOCK_CONNECT==0){ # attempt to do a timeout alarm...
  4993.  
  4994.             eval {
  4995.  
  4996.                 local $SIG{ALRM} = sub { die "timeout\n" };
  4997.  
  4998.                 eval {alarm($TIMEOUT)};
  4999.  
  5000.                 if(!connect($$Z[0],sockaddr_in($cport,$$Z[2]))){
  5001.  
  5002.                     alarm(0);
  5003.  
  5004.                     die("no_connect\n"); }
  5005.  
  5006.                 eval {alarm(0)};
  5007.  
  5008.             };
  5009.  
  5010.             if($@ || !(defined $$Z[0])){
  5011.  
  5012.                 $$hout{'whisker'}->{'error'}="Can't connect to $cwhat";
  5013.  
  5014.                 return 2;
  5015.  
  5016.         }    }
  5017.  
  5018.  
  5019.  
  5020.         binmode($$Z[0]); # stupid Windows
  5021.  
  5022.         # same as IO::Handle->autoflush(1), without importing 1000+ lines
  5023.  
  5024.         my $S=select($$Z[0]); 
  5025.  
  5026.         $|++; select($S);
  5027.  
  5028.     }
  5029.  
  5030.  
  5031.  
  5032.     $$Z[1]=1; $$Z[7]++;
  5033.  
  5034.  
  5035.  
  5036.     if($$W{'ssl'}>0){
  5037.  
  5038.  
  5039.  
  5040.         if($LW::LW_SSL_LIB==1){
  5041.  
  5042.  
  5043.  
  5044.             if($PROXY){ # handle the proxy CONNECT stuff...
  5045.  
  5046.             my $SSL_CONNECT = "CONNECT $$W{'host'}".
  5047.  
  5048.             ":$$W{'port'}/ HTTP/1.0\n\n";
  5049.  
  5050.             syswrite($$Z[0],$SSL_CONNECT, length($SSL_CONNECT)); }
  5051.  
  5052.  
  5053.  
  5054.         Net::SSLeay::set_fd($$Z[4], fileno($$Z[0]));
  5055.  
  5056.         Net::SSLeay::set_session($$Z[4],$$Z[9]) if(defined $$Z[9]);
  5057.  
  5058.         if(! (Net::SSLeay::connect($$Z[4])) ){
  5059.  
  5060.             $$hout{'whisker'}->{'error'}="SSL_connect error: $!";
  5061.  
  5062.             sock_close($$Z[0],$$Z[4]); return 2;}
  5063.  
  5064.  
  5065.  
  5066.         if(defined $$W{'save_ssl_info'} && 
  5067.  
  5068.                 $$W{'save_ssl_info'}>0){
  5069.  
  5070.             ssl_save_info($hout,$$Z[4]); }
  5071.  
  5072.         my $x=Net::SSLeay::ctrl($$Z[4],6,0,'');
  5073.  
  5074.         $$Z[9]=Net::SSLeay::get_session($$Z[4]) unless(defined $$W{'ssl_resume'} &&
  5075.  
  5076.             $$W{'ssl_resume'}==0);
  5077.  
  5078.         }
  5079.  
  5080.  
  5081.  
  5082.     } else {
  5083.  
  5084.         $$Z[4]=undef;
  5085.  
  5086.     }
  5087.  
  5088.  }
  5089.  
  5090.  
  5091.  
  5092.  if(defined $$W{'ids_session_splice'} &&
  5093.  
  5094.             $$W{'ids_session_splice'}>0 &&
  5095.  
  5096.         $$W{'ssl'}==0){ # no session_spice over ssl
  5097.  
  5098.     setsockopt($$Z[0],SOL_SOCKET,SO_SNDLOWAT,1);
  5099.  
  5100.     @c=split(//, &http_req2line($hin));
  5101.  
  5102.     # notice we bypass queueing here, in order to trickle the packets
  5103.  
  5104.     my $ss;
  5105.  
  5106.     foreach $c (@c){ 
  5107.  
  5108.         $ss=syswrite($$Z[0],$c,1); # char size assumed to be 1
  5109.  
  5110.         if(!defined $ss || $ss==0){
  5111.  
  5112.             $$hout{'whisker'}->{'error'}="Error sending session splice request to server";
  5113.  
  5114.             sock_close($$Z[0],$$Z[4]); return 1;
  5115.  
  5116.         }
  5117.  
  5118.         select(undef,undef,undef,.1);
  5119.  
  5120.     }
  5121.  
  5122.  } else {
  5123.  
  5124.      http_queue(http_req2line($hin)); }
  5125.  
  5126.  
  5127.  
  5128.  $$Z[8]++;
  5129.  
  5130.  
  5131.  
  5132.  if($$W{'http_ver'} ne '0.9'){
  5133.  
  5134.     my %SENT;
  5135.  
  5136.     if(defined $$W{'header_order'} && ref($$W{'header_order'})){
  5137.  
  5138.     foreach (@{$$W{'header_order'}}){
  5139.  
  5140.         next if($_ eq '' || $_ eq 'whisker');
  5141.  
  5142.         if(ref($$hin{$_})){
  5143.  
  5144.             $SENT{$_}||=0;
  5145.  
  5146.             my $v=$$hin{$_}->[$SENT{$_}];
  5147.  
  5148.             http_queue("$_: $v$$W{'http_eol'}");
  5149.  
  5150.         } else {
  5151.  
  5152.             http_queue("$_: $$hin{$_}$$W{'http_eol'}");
  5153.  
  5154.         }
  5155.  
  5156.         $SENT{$_}++;
  5157.  
  5158.     }
  5159.  
  5160.     }
  5161.  
  5162.  
  5163.  
  5164.     foreach (keys %$hin){
  5165.  
  5166.     next if($_ eq '' || $_ eq 'whisker');
  5167.  
  5168.     next if(defined $SENT{$_});
  5169.  
  5170.     if(ref($$hin{$_})){ # header with multiple values
  5171.  
  5172.         my $key=$_;
  5173.  
  5174.         foreach (@{$$hin{$key}}){
  5175.  
  5176.           http_queue("$key: $_$$W{'http_eol'}");}
  5177.  
  5178.     } else { # normal header
  5179.  
  5180.         http_queue("$_: $$hin{$_}$$W{'http_eol'}");
  5181.  
  5182.     }
  5183.  
  5184.     }
  5185.  
  5186.  
  5187.  
  5188.     if(defined $$W{'raw_header_data'}){
  5189.  
  5190.     http_queue($$W{'raw_header_data'});}
  5191.  
  5192.  
  5193.  
  5194.     http_queue($$W{'http_eol'});
  5195.  
  5196.  
  5197.  
  5198.     if(defined $$W{'data'}){ 
  5199.  
  5200.     http_queue($$W{'data'});}
  5201.  
  5202.  
  5203.  
  5204.  } # http 0.9 support
  5205.  
  5206.  
  5207.  
  5208.  # take a MD5 of queue, if wanted
  5209.  
  5210.  if(defined $$W{'queue_md5'}){
  5211.  
  5212.     $$hout{'whisker'}->{'queue_md5'}= LW::md5($$Z[5]);
  5213.  
  5214.  }
  5215.  
  5216.  
  5217.  
  5218.  
  5219.  
  5220.  # all data is wrangled...actually send it now
  5221.  
  5222.  if($res=http_queue_send($$Z[0],$$Z[4])){
  5223.  
  5224.     $$hout{'whisker'}->{'error'}="Error sending request to server: $res";
  5225.  
  5226.     sock_close($$Z[0],$$Z[4]); $$Z[1]=0; return 1;}
  5227.  
  5228.  
  5229.  
  5230.  undef $vin;
  5231.  
  5232.  if(defined $$Z[4]){
  5233.  
  5234.     if($LW::LW_SSL_LIB==1){ # Net::SSLeay
  5235.  
  5236.          shutdown $$Z[0], 1; 
  5237.  
  5238.          vec($vin,fileno($$Z[0]),1)=1;
  5239.  
  5240.     } else { # Net::SSL
  5241.  
  5242.         shutdown $$Z[4], 1;
  5243.  
  5244.         vec($vin,fileno($$Z[4]),1)=1;
  5245.  
  5246.     }
  5247.  
  5248.  } else {
  5249.  
  5250.     vec($vin,fileno($$Z[0]),1)=1;      
  5251.  
  5252.  }
  5253.  
  5254.  
  5255.  
  5256.  if(!select($vin,undef,undef,$TIMEOUT)){
  5257.  
  5258.     $$hout{'whisker'}->{'error'}="Server read timed out";
  5259.  
  5260.     sock_close($$Z[0],$$Z[4]); $$Z[1]=0; return 1;}
  5261.  
  5262.  
  5263.  
  5264. my ($LC,$CL,$TE,$CO)=('',-1,'',''); # extra header stuff
  5265.  
  5266.  
  5267.  
  5268. $$hout{'whisker'}->{'lowercase_incoming_headers'} = 
  5269.  
  5270.     $$W{'lowercase_incoming_headers'};
  5271.  
  5272.  
  5273.  
  5274. if($$W{'http_ver'} ne '0.9'){
  5275.  
  5276.  
  5277.  
  5278.  do { # catch '100 Continue' responses
  5279.  
  5280.   $resp=sock_getline($$Z[0],$$Z[4]);
  5281.  
  5282.   #$resp=~tr/\r\n//d if(defined $resp);
  5283.  
  5284.  
  5285.  
  5286.   if(!defined $resp){
  5287.  
  5288.     $$hout{'whisker'}->{'error'}='Error reading HTTP response';
  5289.  
  5290.     if($!){ # this should be left over from sysread via sock_getline
  5291.  
  5292.         $$hout{'whisker'}->{'error'}.=": $!"; }
  5293.  
  5294.     $$hout{'whisker'}->{'data'}=$$Z[6];
  5295.  
  5296.     sock_close($$Z[0],$$Z[4]); $$Z[1]=0; # otherwise bad crap lingers
  5297.  
  5298.     return 1;}
  5299.  
  5300.  
  5301.  
  5302.   if(defined $$W{'save_raw_headers'}){
  5303.  
  5304.     $$hout{'whisker'}->{'raw_header_data'}.=$resp;}
  5305.  
  5306.  
  5307.  
  5308.   if($resp!~/^HTTP\/([0-9.]{3})[ \t]+(\d+)[ \t]{0,1}(.*?)[\r\n]+/){
  5309.  
  5310.     $$hout{'whisker'}->{'error'}="Invalid HTTP response: $resp";
  5311.  
  5312.     # let's save the incoming data...we might want it
  5313.  
  5314.     $$hout{'whisker'}->{'data'}=$resp;
  5315.  
  5316.     while(defined ($_=sock_getline($$Z[0],$$Z[4]))){ 
  5317.  
  5318.         $$hout{'whisker'}->{'data'}.=$_;}
  5319.  
  5320.     # normally we'd check the results to see if socket is closed, but
  5321.  
  5322.     # we close it anyway, so it doesn't matter
  5323.  
  5324.     sock_close($$Z[0],$$Z[4]); $$Z[1]=0; # otherwise bad crap lingers
  5325.  
  5326.     return 1;}
  5327.  
  5328.  
  5329.  
  5330.   $$hout{'whisker'}->{'http_ver'}    = $1;
  5331.  
  5332.   $$hout{'whisker'}->{'http_resp'}    = $2;
  5333.  
  5334.   $$hout{'whisker'}->{'http_resp_message'}= $3;
  5335.  
  5336.   $$hout{'whisker'}->{'code'}        = $2;
  5337.  
  5338.  
  5339.  
  5340.   $$hout{'whisker'}->{'100_continue'}++ if($2 == 100);
  5341.  
  5342.  
  5343.  
  5344.   while(defined ($_=sock_getline($$Z[0],$$Z[4]))){ # check pertinent headers
  5345.  
  5346.  
  5347.  
  5348.     if(defined $$W{'save_raw_headers'}){
  5349.  
  5350.         $$hout{'whisker'}->{'raw_header_data'}.=$_;}
  5351.  
  5352.  
  5353.  
  5354.     $_=~s/[\r]{0,1}\n$//; # anchored regex, so it's fast
  5355.  
  5356.     last if ($_ eq ''); # acceptable assumption case?
  5357.  
  5358.  
  5359.  
  5360.     my $l2=index($_,':'); # this is faster than regex
  5361.  
  5362.     $a=substr($_,0,$l2); 
  5363.  
  5364.     $b=substr($_,$l2+1);
  5365.  
  5366.     $b=~s/^([ \t]*)//; # anchored regex, so it's fast
  5367.  
  5368.  
  5369.  
  5370.     $hout{'whisker'}->{'abnormal_header_spacing'}++ if($1 ne ' ');
  5371.  
  5372.  
  5373.  
  5374.     $LC = lc($a);
  5375.  
  5376.     next         if($LC eq 'whisker');
  5377.  
  5378.     $TE = lc($b) if($LC eq 'transfer-encoding');
  5379.  
  5380.     $CL = $b     if($LC eq 'content-length');
  5381.  
  5382.     $CO = lc($b) if($LC eq 'connection');
  5383.  
  5384.  
  5385.  
  5386.     if($$W{'lowercase_incoming_headers'}>0){
  5387.  
  5388.         $a=$LC;
  5389.  
  5390.     } elsif($$W{'normalize_incoming_headers'}>0){ 
  5391.  
  5392.                 $a=~s/(-[a-z])/uc($1)/eg;
  5393.  
  5394.      }
  5395.  
  5396.  
  5397.  
  5398.     # save the received header order, in case we're curious
  5399.  
  5400.     push(@{$$hout{'whisker'}->{'recv_header_order'}},$a);
  5401.  
  5402.  
  5403.  
  5404.     if(defined $$hout{$a} && $$W{'ignore_duplicate_headers'}!=1){
  5405.  
  5406.       if(!ref($$hout{$a})){
  5407.  
  5408.         my $temp=$$hout{$a};
  5409.  
  5410.         delete $$hout{$a};
  5411.  
  5412.         push(@{$$hout{$a}},$temp);
  5413.  
  5414.       }
  5415.  
  5416.       push(@{$$hout{$a}},$b);
  5417.  
  5418.     } else {
  5419.  
  5420.       $$hout{$a}=$b;
  5421.  
  5422.   }    }
  5423.  
  5424.  
  5425.  
  5426.   # did we have a socket error?
  5427.  
  5428.   if($!){
  5429.  
  5430.     $hout{'whisker'}->{'error'}='Error in reading response/headers';
  5431.  
  5432.     sock_close($$Z[0],$$Z[4]); $$Z[1]=0; return 1; }
  5433.  
  5434.  
  5435.  
  5436.   if( $CO eq '' ){ # do whatever the client wanted
  5437.  
  5438.     $CO = (defined $$hin{'Connection'}) ? lc($$hin{'Connection'}) : 
  5439.  
  5440.         'close'; }
  5441.  
  5442.  
  5443.  
  5444.  } while($$hout{'whisker'}->{'http_resp'}==100);
  5445.  
  5446.  
  5447.  
  5448. } else { # http ver 0.9, we need to fake it
  5449.  
  5450.  # Keep in mind lame broken servers, like IIS, still send headers for 
  5451.  
  5452.  # 0.9 requests; the headers are treated as data.  Also keep in mind
  5453.  
  5454.  # that if the server doesn't support HTTP 0.9 requests, it will spit
  5455.  
  5456.  # back an HTTP 1.0 response header.  User is responsible for figuring
  5457.  
  5458.  # this out himself.
  5459.  
  5460.  $$hout{'whisker'}->{'http_ver'}='0.9';
  5461.  
  5462.  $$hout{'whisker'}->{'http_resp'}='200';
  5463.  
  5464.  $$hout{'whisker'}->{'http_resp_message'}='';
  5465.  
  5466. }
  5467.  
  5468.  
  5469.  
  5470.  if($$W{'force_bodysnatch'} || ( $$W{'method'} ne 'HEAD' && 
  5471.  
  5472.     $$hout{'whisker'}->{'http_resp'}!=206 &&
  5473.  
  5474.     $$hout{'whisker'}->{'http_resp'}!=102)){
  5475.  
  5476.   if ($TE eq 'chunked') { 
  5477.  
  5478.     if(!defined ($a=sock_getline($$Z[0],$$Z[4]))){
  5479.  
  5480.         $$hout{'whisker'}->{'error'}='Error reading chunked data length';
  5481.  
  5482.         sock_close($$Z[0],$$Z[4]); $$Z[1]=0; return 1;}
  5483.  
  5484.     $a=~tr/a-fA-F0-9//cd; $CL=hex($a); 
  5485.  
  5486.     $$hout{'whisker'}->{'data'}='';
  5487.  
  5488.     while($CL!=0) { # chunked sucks
  5489.  
  5490.         if(!defined ($temp=sock_get($$Z[0],$$Z[4],$CL))){
  5491.  
  5492.             $$hout{'whisker'}->{'error'}="Error reading chunked data: $!";
  5493.  
  5494.             sock_close($$Z[0],$$Z[4]); $$Z[1]=0; return 1;}
  5495.  
  5496.         $$hout{'whisker'}->{'data'}=$$hout{'whisker'}->{'data'} . $temp;
  5497.  
  5498.         $temp=sock_getline($$Z[0], $$Z[4]);
  5499.  
  5500.         ($temp=sock_getline($$Z[0], $$Z[4])) if(defined $temp &&
  5501.  
  5502.             $temp=~/^[\r\n]*$/);
  5503.  
  5504.         if(!defined $temp){ # this will catch errors in either sock_getline
  5505.  
  5506.             $$hout{'whisker'}->{'error'}="Error reading chunked data: $!";
  5507.  
  5508.             sock_close($$Z[0],$$Z[4]); $$Z[1]=0; return 1;}
  5509.  
  5510.         $temp=~tr/a-fA-F0-9//cd; $CL=hex($temp);}
  5511.  
  5512.     # read in trailer headers
  5513.  
  5514.     while(defined ($_=sock_getline($$Z[0],$$Z[4]))){ tr/\r\n//d; last if($_ eq ''); }
  5515.  
  5516.     # Hmmmm...error, but we should have full body.  Don't return error
  5517.  
  5518.     if($!){ $$Z[1]=0; sock_close($$Z[0],$$Z[4]); }
  5519.  
  5520.   } else {
  5521.  
  5522.      if ($CL != -1) {
  5523.  
  5524.         if(!defined ($temp=sock_get($$Z[0],$$Z[4],$CL))){
  5525.  
  5526.             $$hout{'whisker'}->{'error'}="Error reading data: $!";
  5527.  
  5528.             sock_close($$Z[0],$$Z[4]); $$Z[1]=0; return 1;}
  5529.  
  5530.     } else {  # Yuck...read until server stops sending....
  5531.  
  5532.         $temp=sock_getall($$Z[0],$$Z[4]);
  5533.  
  5534.         # we go until we puke, so close socket and don't return error
  5535.  
  5536.         sock_close($$Z[0],$$Z[4]); $$Z[1]=0;
  5537.  
  5538.     }
  5539.  
  5540.     $$hout{'whisker'}->{'data'}=$temp; 
  5541.  
  5542.   }
  5543.  
  5544.  } # /method ne HEAD && http_resp ne 206 or 102/
  5545.  
  5546.  
  5547.  
  5548.  if(($CO ne 'keep-alive' || ( defined $$hin{'Connection'} &&
  5549.  
  5550.         lc($$hin{'Connection'}) eq 'close')) && $$W{'force_open'}!=1){
  5551.  
  5552.     $$Z[1]=0; sock_close($$Z[0],$$Z[4]); 
  5553.  
  5554.  }     
  5555.  
  5556.  
  5557.  
  5558.  # this way we know what the state *would* have been...
  5559.  
  5560.  $$hout{'whisker'}->{'sockstate'}=$$Z[1];
  5561.  
  5562.  if($$W{'force_close'}>0) {
  5563.  
  5564.     $$Z[1]=0; sock_close($$Z[0],$$Z[4]); } 
  5565.  
  5566.  
  5567.  
  5568.  if($$W{'ssl'}>0){ # we don't reuse SSL sockets
  5569.  
  5570.     $$Z[1]=0; sock_close($$Z[0],$$Z[4]); }
  5571.  
  5572.  
  5573.  
  5574.  $$hout{'whisker'}->{'stats_reqs'}=$$Z[8];
  5575.  
  5576.  $$hout{'whisker'}->{'stats_syns'}=$$Z[7];
  5577.  
  5578.  $$hout{'whisker'}->{'error'}=''; # no errors
  5579.  
  5580.  return 0;
  5581.  
  5582. }
  5583.  
  5584.  
  5585.  
  5586.  
  5587.  
  5588. ##################################################################
  5589.  
  5590.  
  5591.  
  5592. =pod
  5593.  
  5594.  
  5595.  
  5596. =head1 - Function: LW::http_req2line (INTERNAL)
  5597.  
  5598.   
  5599.  
  5600. Params: \%hin, $switch
  5601.  
  5602. Return: $request
  5603.  
  5604.  
  5605.  
  5606. req2line is used internally by LW::http_do_request, as well as provides a
  5607.  
  5608. convienient way to turn a %hin configuration into an actual HTTP request
  5609.  
  5610. line.  If $switch is set to 1, then the returned $request will be the URI
  5611.  
  5612. only ('/requested/page.html'), versus the entire HTTP request ('GET
  5613.  
  5614. /requested/page.html HTTP/1.0\n\n').  Also, if the 'full_request_override'
  5615.  
  5616. whisker config variable is set in %hin, then it will be returned instead
  5617.  
  5618. of the constructed URI.
  5619.  
  5620.  
  5621.  
  5622. =cut
  5623.  
  5624.  
  5625.  
  5626. sub http_req2line {
  5627.  
  5628.  my ($S,$hin,$UO)=('',@_);
  5629.  
  5630.  $UO||=0; # shut up -w warning
  5631.  
  5632.  
  5633.  
  5634.  # notice: full_request_override can play havoc with proxy settings
  5635.  
  5636.  if(defined $$hin{'whisker'}->{'full_request_override'}){
  5637.  
  5638.     return $$hin{'whisker'}->{'full_request_override'};
  5639.  
  5640.  
  5641.  
  5642.  } else { # notice the components of a request--this is for flexibility
  5643.  
  5644.  
  5645.  
  5646.     if($UO!=1){$S.=     $$hin{'whisker'}->{'method'}.
  5647.  
  5648.                 $$hin{'whisker'}->{'method_postfix'}.
  5649.  
  5650.                 $$hin{'whisker'}->{'req_spacer'};
  5651.  
  5652.     
  5653.  
  5654.         if($$hin{'whisker'}->{'include_host_in_uri'}>0){
  5655.  
  5656.             $S.=    'http://';
  5657.  
  5658.  
  5659.  
  5660.             if(defined $$hin{'whisker'}->{'uri_user'}){
  5661.  
  5662.             $S.=    $$hin{'whisker'}->{'uri_user'};
  5663.  
  5664.             if(defined $$hin{'whisker'}->{'uri_password'}){
  5665.  
  5666.                 $S.=    ':'.$$hin{'whisker'}->{'uri_user'};
  5667.  
  5668.             }
  5669.  
  5670.             $S.=    '@';
  5671.  
  5672.             }
  5673.  
  5674.  
  5675.  
  5676.             $S.=    $$hin{'whisker'}->{'host'}.
  5677.  
  5678.                 ':'.$$hin{'whisker'}->{'port'};}}
  5679.  
  5680.  
  5681.  
  5682.     $S.=    $$hin{'whisker'}->{'uri_prefix'}.
  5683.  
  5684.         $$hin{'whisker'}->{'uri'}.
  5685.  
  5686.         $$hin{'whisker'}->{'uri_postfix'};
  5687.  
  5688.  
  5689.  
  5690.     if(defined $$hin{'whisker'}->{'uri_param'}){
  5691.  
  5692.         $S.=     $$hin{'whisker'}->{'uri_param_sep'}.
  5693.  
  5694.             $$hin{'whisker'}->{'uri_param'};}
  5695.  
  5696.  
  5697.  
  5698.     if($UO!=1){  if($$hin{'whisker'}->{'http_ver'} ne '0.9'){
  5699.  
  5700.             $S.=     $$hin{'whisker'}->{'req_spacer2'}.'HTTP/'.
  5701.  
  5702.                 $$hin{'whisker'}->{'http_ver'}.
  5703.  
  5704.                 $$hin{'whisker'}->{'http_req_trailer'};}
  5705.  
  5706.             $S.=    $$hin{'whisker'}->{'http_eol'};}}
  5707.  
  5708.  return $S;}
  5709.  
  5710.  
  5711.  
  5712.  
  5713.  
  5714.  
  5715.  
  5716. ##################################################################
  5717.  
  5718.  
  5719.  
  5720. =pod
  5721.  
  5722.  
  5723.  
  5724. =head1 - Function LW::sock_close (INTERNAL)
  5725.  
  5726.    
  5727.  
  5728. Params: $socket_file_descriptor, $SSL_THINGY
  5729.  
  5730. Return: nothing
  5731.  
  5732.  
  5733.  
  5734. This function will close the indicated socket and SSL connection (if 
  5735.  
  5736. necessary).  They are wrapped in eval()s to make sure if the functions 
  5737.  
  5738. puke, it doesn't kill the entire program.
  5739.  
  5740.  
  5741.  
  5742. =cut
  5743.  
  5744.  
  5745.  
  5746. sub sock_close {
  5747.  
  5748.     my ($fd,$ssl)=@_;
  5749.  
  5750.  
  5751.  
  5752.     if(defined $ssl){
  5753.  
  5754.         if($LW::LW_SSL_LIB==1){ # Net::SSLeay
  5755.  
  5756.         eval "&Net::SSLeay::free($ssl)";
  5757.  
  5758. #        eval "&Net::SSLeay::CTX_free($$Z[3])";
  5759.  
  5760.         } else { # Net::SSL
  5761.  
  5762.         eval { close($ssl) }; # is that right for Net::SSL?
  5763.  
  5764.         }
  5765.  
  5766.     }
  5767.  
  5768.     eval { close($fd); };
  5769.  
  5770.  
  5771.  
  5772.     $$Z[4]=undef;
  5773.  
  5774. }
  5775.  
  5776.  
  5777.  
  5778. ##################################################################
  5779.  
  5780.  
  5781.  
  5782. =pod
  5783.  
  5784.  
  5785.  
  5786. =head1 - Function LW::sock_valid (INTERNAL)
  5787.  
  5788.    
  5789.  
  5790. Params: $Z reference, \%hin, \%hout
  5791.  
  5792. Return: 1 if socket valid, 0 if socket disconnected
  5793.  
  5794.  
  5795.  
  5796. This is an internal function used to determine if a socket is
  5797.  
  5798. still good (i.e. the other END hasn't closed the connection).
  5799.  
  5800. This really only applies to persistent (Keep-Alive) connections.
  5801.  
  5802.  
  5803.  
  5804. This function is not intended for external use.
  5805.  
  5806.  
  5807.  
  5808. =cut
  5809.  
  5810.  
  5811.  
  5812. sub sock_valid {
  5813.  
  5814.     my ($z,$Hin,$Hout)=@_;
  5815.  
  5816.  
  5817.  
  5818.     my $slurp=$$Hin{'whisker'}->{'trailing_slurp'};
  5819.  
  5820.     my ($o,$vin)=(undef,'');
  5821.  
  5822.  
  5823.  
  5824.     return 0 if(defined $$z[3]); # we don't do SSL yet
  5825.  
  5826.  
  5827.  
  5828.     # closed socket sets read flag (and so does waiting data)
  5829.  
  5830.      vec($vin,fileno($$z[0]),1)=1;
  5831.  
  5832.      if(select(($o=$vin),undef,undef,.01)){ # we have data to read
  5833.  
  5834.         my ($hold, $res);
  5835.  
  5836.  
  5837.  
  5838.         do {
  5839.  
  5840.             $res = sysread($$z[0], $hold, 4096);
  5841.  
  5842.             $$z[6].=$hold if($slurp==0); # save to queue
  5843.  
  5844.             $$Hout{'whisker'}->{'slurped'}.="$hold\0"
  5845.  
  5846.                 if($slurp==1); # save to hout hash
  5847.  
  5848.             # fall through value of 2 doesn't do anything
  5849.  
  5850.         } while ($res && select(($o=$vin),undef,undef,.01));
  5851.  
  5852.  
  5853.  
  5854.         if(!defined $res || $res==0){ # error or EOF
  5855.  
  5856.             return 0;
  5857.  
  5858.         }
  5859.  
  5860.     }
  5861.  
  5862.     
  5863.  
  5864.     return 1;
  5865.  
  5866. }
  5867.  
  5868.  
  5869.  
  5870. ##################################################################
  5871.  
  5872.  
  5873.  
  5874. =pod
  5875.  
  5876.  
  5877.  
  5878. =head1 - Function: LW::sock_getline (INTERNAL)
  5879.  
  5880.    
  5881.  
  5882. Params: $socket_file_descriptor, $SSL_THINGY
  5883.  
  5884. Return: $string, undef on error (timeout)
  5885.  
  5886.  
  5887.  
  5888. This function is used internally to read a line of input (up to a '\n')
  5889.  
  5890. from the given socket file descriptor (regular or SSL).
  5891.  
  5892.  
  5893.  
  5894. This function is not intended for external use.
  5895.  
  5896.  
  5897.  
  5898. =cut
  5899.  
  5900.  
  5901.  
  5902. sub sock_getline { # read from socket w/ timeouts
  5903.  
  5904.         my ($fd,$ssl) = @_;
  5905.  
  5906.         my ($str,$t)=('','');
  5907.  
  5908.  
  5909.  
  5910.         $t = index($$Z[6],"\n",0);
  5911.  
  5912.  
  5913.  
  5914.         while($t < 0){
  5915.  
  5916.                 return undef if &http_queue_read($fd,$ssl);
  5917.  
  5918.                 $t=index($$Z[6],"\n",0);
  5919.  
  5920.         }
  5921.  
  5922.  
  5923.  
  5924.     # MEMLEAK: use following lines; comment out SPEEDUP and LEGACY lines
  5925.  
  5926.     # my $r;
  5927.  
  5928.     # ($r,$$Z[6])=unpack('A'.($t+1).'A*',$$Z[6]);
  5929.  
  5930.     # return $r;
  5931.  
  5932.  
  5933.  
  5934.     # SPEEDUP: use following line; comment out MEMLEAK and LEGACY lines
  5935.  
  5936.     # return substr($$Z[6],0,$t+1,'');
  5937.  
  5938.  
  5939.  
  5940.     # LEGACY: use following lines; comment out MEMLEAK and SPEEDUP lines
  5941.  
  5942.     my $r = substr($$Z[6],0,$t+1);
  5943.  
  5944.     substr($$Z[6],0,$t+1)='';
  5945.  
  5946.     return $r;
  5947.  
  5948. }
  5949.  
  5950.  
  5951.  
  5952. ##################################################################
  5953.  
  5954.  
  5955.  
  5956. =pod
  5957.  
  5958.  
  5959.  
  5960. =head1 - Function: LW::sock_get (INTERNAL)
  5961.  
  5962.    
  5963.  
  5964. Params: $socket_file_descriptor, $SSL_THINGY, required $amount
  5965.  
  5966. Return: $string, undef on error
  5967.  
  5968.  
  5969.  
  5970. This function is used internally to read input from the given socket 
  5971.  
  5972. file descriptor (regular or SSL).  Will abort/error if $amount is not
  5973.  
  5974. available.
  5975.  
  5976.  
  5977.  
  5978. This function is not intended for external use.
  5979.  
  5980.  
  5981.  
  5982. =cut
  5983.  
  5984.  
  5985.  
  5986. sub sock_get { # read from socket w/ timeouts
  5987.  
  5988.         my ($fd,$ssl,$amount) = @_;
  5989.  
  5990.         my ($str,$t)=('','');
  5991.  
  5992.  
  5993.  
  5994.     while($amount > length($$Z[6])){
  5995.  
  5996.                 return undef if &http_queue_read($fd,$ssl);
  5997.  
  5998.     }
  5999.  
  6000.  
  6001.  
  6002.     # MEMLEAK: use following lines; comment out SPEEDUP and LEGACY lines
  6003.  
  6004.     # my $r;
  6005.  
  6006.     # ($r,$$Z[6])=unpack('A'.$amount.'A*',$$Z[6]);
  6007.  
  6008.     # return $r;
  6009.  
  6010.  
  6011.  
  6012.     # SPEEDUP: use following line; comment out MEMLEAK and LEGACY lines
  6013.  
  6014.     # return substr($$Z[6],0,$amount,'');
  6015.  
  6016.  
  6017.  
  6018.     # LEGACY: use following lines; comment out MEMLEAK and SPEEDUP lines
  6019.  
  6020.     my $r = substr($$Z[6],0,$amount);
  6021.  
  6022.     substr($$Z[6],0,$amount)='';
  6023.  
  6024.     return $r;
  6025.  
  6026. }
  6027.  
  6028.  
  6029.  
  6030. ##################################################################
  6031.  
  6032.  
  6033.  
  6034. =pod
  6035.  
  6036.  
  6037.  
  6038. =head1 - Function: LW::sock_getall (INTERNAL)
  6039.  
  6040.    
  6041.  
  6042. Params: $socket_file_descriptor, $SSL_THINGY
  6043.  
  6044. Return: $string
  6045.  
  6046.  
  6047.  
  6048. This function is used internally to read input from the given socket 
  6049.  
  6050. file descriptor (regular or SSL).  It will return everything received
  6051.  
  6052. until an error (no data or real error) occurs.
  6053.  
  6054.  
  6055.  
  6056. This function is not intended for external use.
  6057.  
  6058.  
  6059.  
  6060. =cut
  6061.  
  6062.  
  6063.  
  6064. sub sock_getall {
  6065.  
  6066.         my ($fd,$ssl) = @_;
  6067.  
  6068.         1 while( !(&http_queue_read($fd,$ssl)) );
  6069.  
  6070.         return $$Z[6];
  6071.  
  6072. }
  6073.  
  6074.  
  6075.  
  6076. ##################################################################
  6077.  
  6078.  
  6079.  
  6080. =pod
  6081.  
  6082.  
  6083.  
  6084. =head1 - Function: LW::http_queue_read (INTERNAL)
  6085.  
  6086.    
  6087.  
  6088. Params: $fd, $ssl
  6089.  
  6090. Return: $character, undef on error (timeout)
  6091.  
  6092.  
  6093.  
  6094. http_queue_read() will put incoming data from the server into 
  6095.  
  6096. the incoming queue for reading.  If there's no more data (or
  6097.  
  6098. on error), it will return 1.  Otherwise it returns 0.
  6099.  
  6100.  
  6101.  
  6102. This function is really for internal use only.
  6103.  
  6104.  
  6105.  
  6106. =cut
  6107.  
  6108.  
  6109.  
  6110. sub http_queue_read {
  6111.  
  6112.     my ($fd,$ssl)=@_;
  6113.  
  6114.     my ($vin, $t)=('','');
  6115.  
  6116.  
  6117.  
  6118.     if(defined $ssl){
  6119.  
  6120.         if($LW::LW_SSL_LIB==1){ # Net::SSLeay
  6121.  
  6122.         local $SIG{ALRM} = sub { die "timeout\n" };
  6123.  
  6124.         local $SIG{PIPE} = sub { die "pipe_error\n" };
  6125.  
  6126.         eval {
  6127.  
  6128.             eval { alarm($TIMEOUT); };
  6129.  
  6130.             $t=Net::SSLeay::read($ssl);
  6131.  
  6132.             eval { alarm(0); };
  6133.  
  6134.         };
  6135.  
  6136.             if($@ || !defined $t || $t eq ''){
  6137.  
  6138.             return 1;}
  6139.  
  6140.         $$Z[6].=$t;
  6141.  
  6142.         } else { # Net::SSL
  6143.  
  6144.         if(!$ssl->read($t,1024)){ return 1;
  6145.  
  6146.         } else { $$Z[6].=$t;}
  6147.  
  6148.         }
  6149.  
  6150.     } else {
  6151.  
  6152.         vec($vin,fileno($fd),1)=1; # wait only so long to read...
  6153.  
  6154.         if(!select($vin,undef,undef,$TIMEOUT)){
  6155.  
  6156.             return 1;}
  6157.  
  6158.                    if(!sysread($fd,$t,4096)){    return 1; # EOF or error
  6159.  
  6160.         } else {            $$Z[6].=$t;}
  6161.  
  6162.     }
  6163.  
  6164.  
  6165.  
  6166.     return 0;
  6167.  
  6168. }
  6169.  
  6170.  
  6171.  
  6172. ##################################################################
  6173.  
  6174.  
  6175.  
  6176. =pod
  6177.  
  6178.  
  6179.  
  6180. =head1 - Function: LW::http_queue_send (INTERNAL)
  6181.  
  6182.    
  6183.  
  6184. Params: $sock, $ssl
  6185.  
  6186. Return: $status_result (undef=ok, else error message)
  6187.  
  6188.  
  6189.  
  6190. This functions sends the current queue (made with LW::http_queue) to the 
  6191.  
  6192. server via the specified SSL or socket connection.
  6193.  
  6194.  
  6195.  
  6196. =cut
  6197.  
  6198.  
  6199.  
  6200. sub http_queue_send { # write to socket
  6201.  
  6202.     my ($fd,$ssl)=@_;
  6203.  
  6204.     my ($v,$wrote,$err)=('');
  6205.  
  6206.  
  6207.  
  6208.     my $len = length($$Z[5]);
  6209.  
  6210.     if(defined $ssl){
  6211.  
  6212.         if($LW::LW_SSL_LIB==1){ # Net::SSLeay
  6213.  
  6214.         ($wrote,$err)=Net::SSLeay::ssl_write_all($ssl,$$Z[5]);
  6215.  
  6216.         return 'Could not send entire data queue' if ($wrote!=$len);
  6217.  
  6218.         return "SSL_write error: $err" unless $wrote;
  6219.  
  6220.         } else { # Net::SSL
  6221.  
  6222.         $ssl->print($$Z[5]);
  6223.  
  6224.         }
  6225.  
  6226.     } else {
  6227.  
  6228.             vec($v,fileno($fd),1)=1;
  6229.  
  6230.          if(!select(undef,$v,undef,.01)){ 
  6231.  
  6232.             return 'Socket write test failed'; }
  6233.  
  6234.         $wrote=syswrite($fd,$$Z[5],length($$Z[5]));
  6235.  
  6236.         return "Error sending data queue: $!" if(!defined $wrote);
  6237.  
  6238.         return 'Could not send entire data queue' if ($wrote != $len);
  6239.  
  6240.     }
  6241.  
  6242.     $$Z[5]=''; return undef;
  6243.  
  6244. }
  6245.  
  6246.  
  6247.  
  6248.  
  6249.  
  6250. ##################################################################
  6251.  
  6252.  
  6253.  
  6254. =pod
  6255.  
  6256.  
  6257.  
  6258. =head1 - Function: LW::http_queue (INTERNAL)
  6259.  
  6260.    
  6261.  
  6262. Params: $data
  6263.  
  6264. Return: nothing
  6265.  
  6266.  
  6267.  
  6268. This function will buffer the output to be sent to the server.  Output is 
  6269.  
  6270. buffered for various reasons (particularlly because of SSL, but also 
  6271.  
  6272. allowing the chance to 'go back' and modify the final output before it's 
  6273.  
  6274. actually sent (after header constructions, etc).
  6275.  
  6276.  
  6277.  
  6278. =cut
  6279.  
  6280.  
  6281.  
  6282. sub http_queue {
  6283.  
  6284.     $$Z[5].= shift;
  6285.  
  6286. }
  6287.  
  6288.  
  6289.  
  6290.  
  6291.  
  6292. ##################################################################
  6293.  
  6294.  
  6295.  
  6296. =pod
  6297.  
  6298.  
  6299.  
  6300. =head1 - Function: LW::http_fixup_request
  6301.  
  6302.    
  6303.  
  6304. Params: $hash_ref
  6305.  
  6306. Return: Nothing
  6307.  
  6308.  
  6309.  
  6310. This function takes a %hin hash reference and makes sure the proper 
  6311.  
  6312. headers exist (for example, it will add the Host: header, calculate the 
  6313.  
  6314. Content-Length: header for POST requests, etc).  For standard requests 
  6315.  
  6316. (i.e. you want the request to be HTTP RFC-compliant), you should call this 
  6317.  
  6318. function right before you call LW::http_do_request.
  6319.  
  6320.  
  6321.  
  6322. =cut
  6323.  
  6324.  
  6325.  
  6326. sub http_fixup_request {
  6327.  
  6328.  my $hin=shift;
  6329.  
  6330.  
  6331.  
  6332.  return if(!(defined $hin && ref($hin)));
  6333.  
  6334.  
  6335.  
  6336.  if($$hin{'whisker'}->{'http_ver'} eq '1.1'){
  6337.  
  6338.      $$hin{'Host'}=$$hin{'whisker'}->{'host'} if(!defined $$hin{'Host'});
  6339.  
  6340.     $$hin{'Connection'}='Keep-Alive' if(!defined $$hin{'Connection'});
  6341.  
  6342.  }
  6343.  
  6344.  
  6345.  
  6346.  if(defined $$hin{'whisker'}->{'data'}){ 
  6347.  
  6348.      if(!defined $$hin{'Content-Length'}){
  6349.  
  6350.         $$hin{'Content-Length'}=length($$hin{'whisker'}->{'data'});}
  6351.  
  6352. #    if(!defined $$hin{'Content-Encoding'}){
  6353.  
  6354. #        $$hin{'Content-Encoding'}='application/x-www-form-urlencoded';}
  6355.  
  6356.  }
  6357.  
  6358.  
  6359.  
  6360.  if(defined $$hin{'whisker'}->{'proxy_host'}){
  6361.  
  6362.     $$hin{'whisker'}->{'include_host_in_uri'}=1;}
  6363.  
  6364.  
  6365.  
  6366. }
  6367.  
  6368.  
  6369.  
  6370. ##################################################################
  6371.  
  6372.  
  6373.  
  6374. =pod
  6375.  
  6376.  
  6377.  
  6378. =head1 - Function: LW::http_reset
  6379.  
  6380.      
  6381.  
  6382. Params: Nothing
  6383.  
  6384. Return: Nothing
  6385.  
  6386.  
  6387.  
  6388. The LW::http_reset function will walk through the %http_host_cache, 
  6389.  
  6390. closing all open sockets and freeing SSL resources.  It also clears
  6391.  
  6392. out the host cache in case you need to rerun everything fresh.
  6393.  
  6394.  
  6395.  
  6396. =cut
  6397.  
  6398.  
  6399.  
  6400. sub http_reset {
  6401.  
  6402.  my $key;
  6403.  
  6404.  
  6405.  
  6406.  foreach $key (keys %http_host_cache){
  6407.  
  6408.      # *Z=$http_host_cache{$key};
  6409.  
  6410.     sock_close($http_host_cache{$key}->[0],
  6411.  
  6412.             $http_host_cache{$key}->[4]);
  6413.  
  6414.     my $x=$http_host_cache{$key}->[3];
  6415.  
  6416.     if(defined $x && $LW::LW_SSL_LIB==1){
  6417.  
  6418.         eval "Net::SSLeay::CTX_free($x)"; }
  6419.  
  6420.     delete $http_host_cache{$key};
  6421.  
  6422.  }
  6423.  
  6424. }
  6425.  
  6426.  
  6427.  
  6428. ##################################################################
  6429.  
  6430.  
  6431.  
  6432. =pod
  6433.  
  6434.  
  6435.  
  6436. =head1 - Function: LW::ssl_save_info (INTERNAL)
  6437.  
  6438.      
  6439.  
  6440. Params: \%hout, $ssl_connection
  6441.  
  6442. Return: Nothing
  6443.  
  6444.  
  6445.  
  6446. This is an internal function used to save various Net::SSLeay
  6447.  
  6448. information into the given hash.  Triggered by setting
  6449.  
  6450. {'whisker'}->{'save_ssl_info'}=1.
  6451.  
  6452.  
  6453.  
  6454. =cut
  6455.  
  6456.  
  6457.  
  6458. sub ssl_save_info {
  6459.  
  6460.     my ($hr,$SSL)=@_;
  6461.  
  6462.     my $cert;
  6463.  
  6464.  
  6465.  
  6466.     return if($LW::LW_SSL_LIB!=1); # only Net::SSLeay used
  6467.  
  6468.     $$hr{'whisker'}->{'ssl_cipher'}=Net::SSLeay::get_cipher($SSL);        
  6469.  
  6470.  
  6471.  
  6472.     if( $cert = Net::SSLeay::get_peer_certificate($SSL)){
  6473.  
  6474.         $$hr{'whisker'}->{'ssl_cert_subject'} = 
  6475.  
  6476.             Net::SSLeay::X509_NAME_oneline(
  6477.  
  6478.                         Net::SSLeay::X509_get_subject_name($cert) );
  6479.  
  6480.  
  6481.  
  6482.         $$hr{'whisker'}->{'ssl_cert_issuer'} = 
  6483.  
  6484.             Net::SSLeay::X509_NAME_oneline(
  6485.  
  6486.                         Net::SSLeay::X509_get_issuer_name($cert) );
  6487.  
  6488.     }
  6489.  
  6490. }
  6491.  
  6492.  
  6493.  
  6494. ##################################################################
  6495.  
  6496.  
  6497.  
  6498. { $SYMCOUNT = 0;
  6499.  
  6500. sub _newsym { # same as Symbol::gensym; taken from libwhisker2
  6501.  
  6502.     my $pkg="LW::";
  6503.  
  6504.     my $name = "_STREAM_" . $SYMCOUNT++;
  6505.  
  6506.     delete $$pkg{$name};
  6507.  
  6508.     return \*{$pkg.$name};
  6509.  
  6510. }}
  6511.  
  6512.  
  6513.  
  6514. ##################################################################
  6515.  
  6516. =pod
  6517.  
  6518.  
  6519.  
  6520. =head1 ++ Sub package: mdx
  6521.  
  6522.  
  6523.  
  6524. The mdx subpackage contains support for making MD4 and MD5 hashes of the 
  6525.  
  6526. given data.  It will attempt to use a faster perl module if installed, 
  6527.  
  6528. and will fall back on the internal perl version (which is *slow* in 
  6529.  
  6530. comparison) if nothing else was found.
  6531.  
  6532.  
  6533.  
  6534. This was written in a few hours using the explanation of Applied 
  6535.  
  6536. Cryptography as the main reference, and Digest::Perl::MD5 as a secondary
  6537.  
  6538. reference.  MD4 was later added, using Authen::NTLM::MD4 as a reference.
  6539.  
  6540.  
  6541.  
  6542. This code should be cross-platform (particularly 64-bit) compatible; if 
  6543.  
  6544. you get errors, contact rfp@wiretrip.net.
  6545.  
  6546.  
  6547.  
  6548. =cut
  6549.  
  6550.  
  6551.  
  6552. ########################################################################
  6553.  
  6554.  
  6555.  
  6556. { # start md5 packaged varbs
  6557.  
  6558. my (@S,@T,@M);
  6559.  
  6560. my $code='';
  6561.  
  6562.  
  6563.  
  6564. =pod
  6565.  
  6566.  
  6567.  
  6568. =head1 - Function: LW::md5
  6569.  
  6570.  
  6571.  
  6572. Params: $data
  6573.  
  6574. Return: $hex_md5_string
  6575.  
  6576.  
  6577.  
  6578. This function takes a data scalar, and composes a MD5 hash of it, and 
  6579.  
  6580. returns it in a hex ascii string.  It will use the fastest MD5 function
  6581.  
  6582. available.
  6583.  
  6584.  
  6585.  
  6586. =cut
  6587.  
  6588.  
  6589.  
  6590. sub md5 {
  6591.  
  6592.     return undef if(!defined $_[0]); # oops, forgot the data
  6593.  
  6594.     return MD5->hexhash($_[0]) if(defined $LW::available{'md5'});
  6595.  
  6596.     return md5_perl($_[0]);
  6597.  
  6598. }
  6599.  
  6600.  
  6601.  
  6602. ########################################################################
  6603.  
  6604.  
  6605.  
  6606. =pod
  6607.  
  6608.  
  6609.  
  6610. =head1 - Function: LW::md5_perl
  6611.  
  6612.  
  6613.  
  6614. Params: $data
  6615.  
  6616. Return: $hex_md5_string
  6617.  
  6618.  
  6619.  
  6620. This is the perl implementation of the MD5 function.  You should use
  6621.  
  6622. the md5() function, which will call this function as a last resort.  
  6623.  
  6624. You can call this function directly if you want to test the code.
  6625.  
  6626.  
  6627.  
  6628. =cut
  6629.  
  6630.  
  6631.  
  6632. sub md5_perl {
  6633.  
  6634.         my $DATA=shift;
  6635.  
  6636.         $DATA=md5_pad($DATA);
  6637.  
  6638.         &md5_init() if(!defined $M[0]);
  6639.  
  6640.         return md5_perl_generated(\$DATA);
  6641.  
  6642. }
  6643.  
  6644.  
  6645.  
  6646. ########################################################################
  6647.  
  6648.  
  6649.  
  6650. =pod
  6651.  
  6652.  
  6653.  
  6654. =head1 - Function: LW::md5_init (INTERNAL)
  6655.  
  6656.  
  6657.  
  6658. Params: nothing
  6659.  
  6660. Return: nothing
  6661.  
  6662.  
  6663.  
  6664. This function generates particular values used in the md5_perl function.
  6665.  
  6666. Normally you do not have to call it, as md5_perl will call it if needed.
  6667.  
  6668. The values here are special MD5 constants.
  6669.  
  6670.  
  6671.  
  6672. =cut
  6673.  
  6674.  
  6675.  
  6676. sub md5_init {
  6677.  
  6678.         return if(defined $S[0]);
  6679.  
  6680.         for(my $i=1; $i<=64; $i++){ $T[$i-1]=int((2**32)*abs(sin($i))); }
  6681.  
  6682.         my @t=(7,12,17,22,5,9,14,20,4,11,16,23,6,10,15,21);
  6683.  
  6684.         for($i=0; $i<64; $i++){  $S[$i]=$t[(int($i/16)*4)+($i%4)]; }
  6685.  
  6686.         @M=(    0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  6687.  
  6688.                 1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,
  6689.  
  6690.                 5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,
  6691.  
  6692.                 0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9 );
  6693.  
  6694.         &md5_generate();
  6695.  
  6696.  
  6697.  
  6698.     # check to see if it works correctly
  6699.  
  6700.     my $TEST=md5_pad('foobar');
  6701.  
  6702.     if( md5_perl_generated(\$TEST) ne 
  6703.  
  6704.         '3858f62230ac3c915f300c664312c63f'){
  6705.  
  6706.         die('Error: MD5 self-test not successful.');
  6707.  
  6708.     }
  6709.  
  6710. }
  6711.  
  6712.  
  6713.  
  6714. ########################################################################
  6715.  
  6716.  
  6717.  
  6718. =pod
  6719.  
  6720.  
  6721.  
  6722. =head1 - Function: LW::md5_pad (INTERNAL)
  6723.  
  6724.  
  6725.  
  6726. Params: $data
  6727.  
  6728. Return: $padded_data
  6729.  
  6730.  
  6731.  
  6732. This function pads the data to be compatible with MD5.
  6733.  
  6734.  
  6735.  
  6736. This function is from Digest::Perl::MD5, and bears the following
  6737.  
  6738. copyrights:
  6739.  
  6740.  
  6741.  
  6742.  Copyright 2000 Christian Lackas, Imperia Software Solutions
  6743.  
  6744.  Copyright 1998-1999 Gisle Aas.
  6745.  
  6746.  Copyright 1995-1996 Neil Winton.
  6747.  
  6748.  Copyright 1991-1992 RSA Data Security, Inc.
  6749.  
  6750.  
  6751.  
  6752. =cut
  6753.  
  6754.  
  6755.  
  6756. sub md5_pad {
  6757.  
  6758.     my $l = length(my $msg=shift() . chr(128));
  6759.  
  6760.     $ msg .= "\0" x (($l%64<=56?56:120)-$l%64);
  6761.  
  6762.     $l=($l-1)*8;
  6763.  
  6764.     $msg .= pack 'VV',$l & 0xffffffff, ($l >> 16 >> 16);
  6765.  
  6766.     return $msg;
  6767.  
  6768. }
  6769.  
  6770.  
  6771.  
  6772. ########################################################################
  6773.  
  6774.  
  6775.  
  6776. =pod
  6777.  
  6778.  
  6779.  
  6780. =head1 - Function: LW::md5_generate (INTERNAL)
  6781.  
  6782.  
  6783.  
  6784. Params: none
  6785.  
  6786. Return: none
  6787.  
  6788.  
  6789.  
  6790. This functions generates and compiles the actual MD5 function.  It's
  6791.  
  6792. faster to have all the operations inline and in order than to call
  6793.  
  6794. functions.  Generating the code via below function cuts the final
  6795.  
  6796. code savings to about 1/50th, with the penalty of having to compile
  6797.  
  6798. it the first time it's used (which takes all of a second or two).
  6799.  
  6800.  
  6801.  
  6802. =cut
  6803.  
  6804.  
  6805.  
  6806. sub md5_generate {
  6807.  
  6808.  my $N='abcddabccdabbcda';
  6809.  
  6810.  my $M='';
  6811.  
  6812.  $M='&0xffffffff' if((1 << 16) << 16); # mask for 64bit systems
  6813.  
  6814.  
  6815.  
  6816.  $code=<<EOT;
  6817.  
  6818.         sub md5_perl_generated {
  6819.  
  6820.     BEGIN { \$^H |= 1; }; # use integer
  6821.  
  6822.         my (\$A,\$B,\$C,\$D)=(0x67452301,0xefcdab89,0x98badcfe,0x10325476);
  6823.  
  6824.         my (\$a,\$b,\$c,\$d,\$t,\$i);
  6825.  
  6826.         my \$dr=shift;
  6827.  
  6828.         my \$l=length(\$\$dr);
  6829.  
  6830.         for my \$L (0 .. ((\$l/64)-1) ) {
  6831.  
  6832.                 my \@D = unpack('V16', substr(\$\$dr, \$L*64,64));
  6833.  
  6834.                 (\$a,\$b,\$c,\$d)=(\$A,\$B,\$C,\$D);
  6835.  
  6836. EOT
  6837.  
  6838.  
  6839.  
  6840.  for($i=0; $i<16; $i++){
  6841.  
  6842.         my ($a,$b,$c,$d)=split('',substr($N,($i%4)*4,4));
  6843.  
  6844.         $code.="\$t=((\$$d^(\$$b\&(\$$c^\$$d)))+\$$a+\$D[$M[$i]]+$T[$i])$M;\n";
  6845.  
  6846.         $code.="\$$a=(((\$t<<$S[$i])|((\$t>>(32-$S[$i]))&((1<<$S[$i])-1)))+\$$b)$M;\n";
  6847.  
  6848.  }
  6849.  
  6850.  for(; $i<32; $i++){
  6851.  
  6852.         my ($a,$b,$c,$d)=split('',substr($N,($i%4)*4,4));
  6853.  
  6854.         $code.="\$t=((\$$c^(\$$d\&(\$$b^\$$c)))+\$$a+\$D[$M[$i]]+$T[$i])$M;\n";
  6855.  
  6856.         $code.="\$$a=(((\$t<<$S[$i])|((\$t>>(32-$S[$i]))&((1<<$S[$i])-1)))+\$$b)$M;\n";
  6857.  
  6858.  }
  6859.  
  6860.  for(; $i<48; $i++){
  6861.  
  6862.         my ($a,$b,$c,$d)=split('',substr($N,($i%4)*4,4));
  6863.  
  6864.         $code.="\$t=((\$$b^\$$c^\$$d)+\$$a+\$D[$M[$i]]+$T[$i])$M;\n";
  6865.  
  6866.         $code.="\$$a=(((\$t<<$S[$i])|((\$t>>(32-$S[$i]))&((1<<$S[$i])-1)))+\$$b)$M;\n";
  6867.  
  6868.  }
  6869.  
  6870.  for(; $i<64; $i++){
  6871.  
  6872.         my ($a,$b,$c,$d)=split('',substr($N,($i%4)*4,4));
  6873.  
  6874.         $code.="\$t=((\$$c^(\$$b|(~\$$d)))+\$$a+\$D[$M[$i]]+$T[$i])$M;\n";
  6875.  
  6876.         $code.="\$$a=(((\$t<<$S[$i])|((\$t>>(32-$S[$i]))&((1<<$S[$i])-1)))+\$$b)$M;\n";
  6877.  
  6878.  }
  6879.  
  6880.  
  6881.  
  6882.  $code.=<<EOT;
  6883.  
  6884.                 \$A=\$A+\$a\&0xffffffff; \$B=\$B+\$b\&0xffffffff;
  6885.  
  6886.                 \$C=\$C+\$c\&0xffffffff; \$D=\$D+\$d\&0xffffffff;
  6887.  
  6888.         } # for
  6889.  
  6890.     return unpack('H*', pack('V4',\$A,\$B,\$C,\$D)); }
  6891.  
  6892. EOT
  6893.  
  6894.  eval "$code";
  6895.  
  6896. }
  6897.  
  6898.  
  6899.  
  6900. } # md5 package container
  6901.  
  6902.  
  6903.  
  6904. ########################################################################
  6905.  
  6906.  
  6907.  
  6908. { # start md4 packaged varbs
  6909.  
  6910. my (@S,@T,@M);
  6911.  
  6912. my $code='';
  6913.  
  6914.  
  6915.  
  6916. =pod
  6917.  
  6918.  
  6919.  
  6920. =head1 - Function: LW::md4
  6921.  
  6922.  
  6923.  
  6924. Params: $data
  6925.  
  6926. Return: $hex_md4_string
  6927.  
  6928.  
  6929.  
  6930. This function takes a data scalar, and composes a MD4 hash of it, and 
  6931.  
  6932. returns it in a hex ascii string.  It will use the fastest MD4 function
  6933.  
  6934. available.
  6935.  
  6936.  
  6937.  
  6938. =cut
  6939.  
  6940.  
  6941.  
  6942. sub md4 {
  6943.  
  6944.     return undef if(!defined $_[0]); # oops, forgot the data
  6945.  
  6946.     md4_perl(@_);
  6947.  
  6948. }
  6949.  
  6950.  
  6951.  
  6952. ########################################################################
  6953.  
  6954.  
  6955.  
  6956. =pod
  6957.  
  6958.  
  6959.  
  6960. =head1 - Function: LW::md4_perl
  6961.  
  6962.  
  6963.  
  6964. Params: $data
  6965.  
  6966. Return: $hex_md4_string
  6967.  
  6968.  
  6969.  
  6970. This is the perl implementation of the MD4 function.  You should use
  6971.  
  6972. the md4() function, which will call this function as a last resort.  
  6973.  
  6974. You can call this function directly if you want to test the code.
  6975.  
  6976.  
  6977.  
  6978. =cut
  6979.  
  6980.  
  6981.  
  6982. sub md4_perl {
  6983.  
  6984.         my $DATA=shift;
  6985.  
  6986.         $DATA=md5_pad($DATA);
  6987.  
  6988.         &md4_init() if(!defined $M[0]);
  6989.  
  6990.         return md4_perl_generated(\$DATA);
  6991.  
  6992. }
  6993.  
  6994.  
  6995.  
  6996. ########################################################################
  6997.  
  6998.  
  6999.  
  7000. =pod
  7001.  
  7002.  
  7003.  
  7004. =head1 - Function: LW::md4_init (INTERNAL)
  7005.  
  7006.  
  7007.  
  7008. Params: none
  7009.  
  7010. Return: none
  7011.  
  7012.  
  7013.  
  7014. This functions generates and compiles the actual MD4 function.  It's
  7015.  
  7016. faster to have all the operations inline and in order than to call
  7017.  
  7018. functions.  Generating the code via below function cuts the final
  7019.  
  7020. code savings to about 1/50th, with the penalty of having to compile
  7021.  
  7022. it the first time it's used (which takes all of a second or two).
  7023.  
  7024.  
  7025.  
  7026. =cut
  7027.  
  7028.  
  7029.  
  7030. sub md4_init {
  7031.  
  7032.  return if(defined $S[0]);
  7033.  
  7034.  my @t=(3,7,11,19,3,5,9,13,3,9,11,15);
  7035.  
  7036.  for($i=0; $i<48; $i++){  $S[$i]=$t[(int($i/16)*4)+($i%4)]; }
  7037.  
  7038.  @M=(    0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
  7039.  
  7040.     0,4,8,12,1,5,9,13,2,6,10,14,3,7,11,15,
  7041.  
  7042.     0,8,4,12,2,10,6,14,1,9,5,13,3,11,7,15 );
  7043.  
  7044.  
  7045.  
  7046.  my $N='abcddabccdabbcda';
  7047.  
  7048.  my $M='';
  7049.  
  7050.  $M='&0xffffffff' if((1 << 16) << 16); # mask for 64bit systems
  7051.  
  7052.  
  7053.  
  7054.  $code=<<EOT;
  7055.  
  7056.         sub md4_perl_generated {
  7057.  
  7058.     BEGIN { \$^H |= 1; }; # use integer
  7059.  
  7060.         my (\$A,\$B,\$C,\$D)=(0x67452301,0xefcdab89,0x98badcfe,0x10325476);
  7061.  
  7062.         my (\$a,\$b,\$c,\$d,\$t,\$i);
  7063.  
  7064.         my \$dr=shift;
  7065.  
  7066.         my \$l=length(\$\$dr);
  7067.  
  7068.         for my \$L (0 .. ((\$l/64)-1) ) {
  7069.  
  7070.                 my \@D = unpack('V16', substr(\$\$dr, \$L*64,64));
  7071.  
  7072.                 (\$a,\$b,\$c,\$d)=(\$A,\$B,\$C,\$D);
  7073.  
  7074. EOT
  7075.  
  7076.  
  7077.  
  7078.  for($i=0; $i<16; $i++){
  7079.  
  7080.         my ($a,$b,$c,$d)=split('',substr($N,($i%4)*4,4));
  7081.  
  7082.     $code.="\$t=((\$$d^(\$$b\&(\$$c^\$$d)))+\$$a+\$D[$M[$i]])$M;\n";
  7083.  
  7084.         $code.="\$$a=(((\$t<<$S[$i])|((\$t>>(32-$S[$i]))&((1<<$S[$i])-1))))$M;\n";
  7085.  
  7086.  }
  7087.  
  7088.  for(; $i<32; $i++){
  7089.  
  7090.         my ($a,$b,$c,$d)=split('',substr($N,($i%4)*4,4));
  7091.  
  7092.      $code.="\$t=(( (\$$b&\$$c)|(\$$b&\$$d)|(\$$c&\$$d) )+\$$a+\$D[$M[$i]]+0x5a827999)$M;\n";
  7093.  
  7094.         $code.="\$$a=(((\$t<<$S[$i])|((\$t>>(32-$S[$i]))&((1<<$S[$i])-1))))$M;\n";
  7095.  
  7096.  }
  7097.  
  7098.  for(; $i<48; $i++){
  7099.  
  7100.         my ($a,$b,$c,$d)=split('',substr($N,($i%4)*4,4));
  7101.  
  7102.      $code.="\$t=(( \$$b^\$$c^\$$d )+\$$a+\$D[$M[$i]]+0x6ed9eba1)$M;\n";
  7103.  
  7104.         $code.="\$$a=(((\$t<<$S[$i])|((\$t>>(32-$S[$i]))&((1<<$S[$i])-1))))$M;\n";
  7105.  
  7106.  }
  7107.  
  7108.  
  7109.  
  7110.  $code.=<<EOT;
  7111.  
  7112.                 \$A=\$A+\$a\&0xffffffff; \$B=\$B+\$b\&0xffffffff;
  7113.  
  7114.                 \$C=\$C+\$c\&0xffffffff; \$D=\$D+\$d\&0xffffffff;
  7115.  
  7116.         } # for
  7117.  
  7118.     return unpack('H*', pack('V4',\$A,\$B,\$C,\$D)); }
  7119.  
  7120. EOT
  7121.  
  7122.  eval "$code";
  7123.  
  7124.  
  7125.  
  7126.  my $TEST=md5_pad('foobar');
  7127.  
  7128.  if( md4_perl_generated(\$TEST) ne 
  7129.  
  7130.     '547aefd231dcbaac398625718336f143'){
  7131.  
  7132.     die('Error: MD4 self-test not successful.');
  7133.  
  7134.  }
  7135.  
  7136. }
  7137.  
  7138.  
  7139.  
  7140. } # md4 package container
  7141.  
  7142.  
  7143.  
  7144.  
  7145.  
  7146. =pod
  7147.  
  7148.  
  7149.  
  7150. =head1 ++ Sub package: multipart
  7151.  
  7152.  
  7153.  
  7154. The multipart subpackage contains various utility functions which
  7155.  
  7156. support making multipart requests (useful for uploading files).
  7157.  
  7158.  
  7159.  
  7160. =cut
  7161.  
  7162.  
  7163.  
  7164. ########################################################################
  7165.  
  7166.  
  7167.  
  7168. =pod
  7169.  
  7170.  
  7171.  
  7172. =head1 - Function: LW::multipart_set
  7173.  
  7174.   
  7175.  
  7176. Params: \%multi_hash, $param_name, $param_value
  7177.  
  7178. Return: nothing
  7179.  
  7180.  
  7181.  
  7182. This function sets the named parameter to the given value within the
  7183.  
  7184. supplied multipart hash.
  7185.  
  7186.  
  7187.  
  7188. =cut
  7189.  
  7190.  
  7191.  
  7192. sub multipart_set {
  7193.  
  7194.     my ($hr,$n,$v)=@_;
  7195.  
  7196.     return if(!ref($hr)); # error check
  7197.  
  7198.     return undef if(!defined $n || $n eq '');
  7199.  
  7200.     $$hr{$n}=$v;    
  7201.  
  7202. }
  7203.  
  7204.  
  7205.  
  7206. ########################################################################
  7207.  
  7208.  
  7209.  
  7210. =pod
  7211.  
  7212.  
  7213.  
  7214. =head1 - Function: LW::multipart_get
  7215.  
  7216.   
  7217.  
  7218. Params: \%multi_hash, $param_name
  7219.  
  7220. Return: $param_value, undef on error
  7221.  
  7222.  
  7223.  
  7224. This function retrieves the named parameter to the given value within the
  7225.  
  7226. supplied multipart hash.  There is a special case where the named
  7227.  
  7228. parameter is actually a file--in which case the resulting value will be
  7229.  
  7230. "\0FILE".  In general, all special values will be prefixed with a NULL
  7231.  
  7232. character.  In order to get a file's info, use multipart_getfile().
  7233.  
  7234.  
  7235.  
  7236. =cut
  7237.  
  7238.  
  7239.  
  7240. sub multipart_get {
  7241.  
  7242.     my ($hr,$n)=@_;
  7243.  
  7244.     return undef if(!ref($hr)); # error check
  7245.  
  7246.     return undef if(!defined $n || $n eq '');
  7247.  
  7248.     return $$hr{$n};
  7249.  
  7250. }
  7251.  
  7252.  
  7253.  
  7254. ########################################################################
  7255.  
  7256.  
  7257.  
  7258. =pod
  7259.  
  7260.  
  7261.  
  7262. =head1 - Function: LW::multipart_setfile
  7263.  
  7264.   
  7265.  
  7266. Params: \%multi_hash, $param_name, $file_path [, $filename]
  7267.  
  7268. Return: undef on error, 1 on success
  7269.  
  7270.  
  7271.  
  7272. NOTE: this function does not actually add the contents of $file_path into
  7273.  
  7274. the %multi_hash; instead, multipart_write() inserts the content when
  7275.  
  7276. generating the final request.
  7277.  
  7278.  
  7279.  
  7280. =cut
  7281.  
  7282.  
  7283.  
  7284. sub multipart_setfile {
  7285.  
  7286.     my ($hr,$n,$path)=(shift,shift,shift);
  7287.  
  7288.     my ($fname)=shift;
  7289.  
  7290.  
  7291.  
  7292.     return undef if(!ref($hr)); # error check
  7293.  
  7294.     return undef if(!defined $n || $n eq '');
  7295.  
  7296.     return undef if(!defined $path);
  7297.  
  7298.     return undef if(! (-e $path && -f $path) );
  7299.  
  7300.  
  7301.  
  7302.     if(!defined $fname){
  7303.  
  7304.         $path=~m/[\\\/]([^\\\/]+)$/;
  7305.  
  7306.         $fname=$1||"whisker-file";
  7307.  
  7308.     }
  7309.  
  7310.  
  7311.  
  7312.     $$hr{$n}="\0FILE";
  7313.  
  7314.     $$hr{"\0$n"}=[$path,$fname];
  7315.  
  7316.     return 1;
  7317.  
  7318. }
  7319.  
  7320.  
  7321.  
  7322. ########################################################################
  7323.  
  7324.  
  7325.  
  7326. =pod
  7327.  
  7328.  
  7329.  
  7330. =head1 - Function: LW::multipart_getfile
  7331.  
  7332.   
  7333.  
  7334. Params: \%multi_hash, $file_param_name
  7335.  
  7336. Return: $path, $name ($path=undef on error)
  7337.  
  7338.  
  7339.  
  7340. LW::multipart_getfile is used to retrieve information for a file
  7341.  
  7342. parameter contained in %multi_hash.  To use this you would most
  7343.  
  7344. likely do:
  7345.  
  7346. ($path,$fname)=LW::multipart_getfile(\%multi, "param_name");
  7347.  
  7348.  
  7349.  
  7350. =cut
  7351.  
  7352.  
  7353.  
  7354. sub multipart_getfile {
  7355.  
  7356.     my ($hr,$n)=@_;
  7357.  
  7358.  
  7359.  
  7360.     return undef if(!ref($hr)); # error check
  7361.  
  7362.     return undef if(!defined $n || $n eq '');
  7363.  
  7364.     return undef if(!defined $$hr{$n} || $$hr{$n} ne "\0FILE");
  7365.  
  7366.  
  7367.  
  7368.     return @{$$hr{"\0$n"}};
  7369.  
  7370. }
  7371.  
  7372.  
  7373.  
  7374. ########################################################################
  7375.  
  7376.  
  7377.  
  7378. =pod
  7379.  
  7380.  
  7381.  
  7382. =head1 - Function: LW::multipart_boundary
  7383.  
  7384.   
  7385.  
  7386. Params: \%multi_hash [, $new_boundary_name]
  7387.  
  7388. Return: $current_boundary_name
  7389.  
  7390.  
  7391.  
  7392. LW::multipart_boundary is used to retrieve, and optionally set, the
  7393.  
  7394. multipart boundary used for the request.
  7395.  
  7396.  
  7397.  
  7398. NOTE: the function does no checking on the supplied boundary, so if 
  7399.  
  7400. you want things to work make sure it's a legit boundary.  Libwhisker
  7401.  
  7402. does *not* prefix it with any '---' characters.
  7403.  
  7404.  
  7405.  
  7406. =cut
  7407.  
  7408.  
  7409.  
  7410. sub multipart_boundary {
  7411.  
  7412.     my ($hr,$new)=@_;
  7413.  
  7414.     my $ret;
  7415.  
  7416.  
  7417.  
  7418.     return undef if(!ref($hr)); # error check
  7419.  
  7420.  
  7421.  
  7422.     if(!defined $$hr{"\0BOUNDARY"}){
  7423.  
  7424.         # create boundary on the fly
  7425.  
  7426.         my $b = uc(LW::utils_randstr(20));
  7427.  
  7428.         my $b2 = '-' x 32;
  7429.  
  7430.         $$hr{"\0BOUNDARY"}="$b2$b";
  7431.  
  7432.     }
  7433.  
  7434.  
  7435.  
  7436.     $ret=$$hr{"\0BOUNDARY"};
  7437.  
  7438.     if(defined $new){
  7439.  
  7440.         $$hr{"\0BOUNDARY"}=$new;
  7441.  
  7442.     }
  7443.  
  7444.  
  7445.  
  7446.     return $ret;
  7447.  
  7448. }
  7449.  
  7450.  
  7451.  
  7452. ########################################################################
  7453.  
  7454.  
  7455.  
  7456. =pod
  7457.  
  7458.  
  7459.  
  7460. =head1 - Function: LW::multipart_write
  7461.  
  7462.   
  7463.  
  7464. Params: \%multi_hash, \%hin_request
  7465.  
  7466. Return: 1 if successful, undef on error
  7467.  
  7468.  
  7469.  
  7470. LW::multipart_write is used to parse and construct the multipart data
  7471.  
  7472. contained in %multi_hash, and place it ready to go in the given whisker
  7473.  
  7474. hash (%hin) structure, to be sent to the server.
  7475.  
  7476.  
  7477.  
  7478. NOTE: file contents are read into the final %hin, so it's possible for
  7479.  
  7480. the hash to get *very* large if you have (a) large file(s).
  7481.  
  7482.  
  7483.  
  7484. =cut
  7485.  
  7486.  
  7487.  
  7488. sub multipart_write {
  7489.  
  7490.     my ($mp,$hr)=@_;
  7491.  
  7492.  
  7493.  
  7494.     return undef if(!ref($mp)); # error check
  7495.  
  7496.     return undef if(!ref($hr)); # error check
  7497.  
  7498.  
  7499.  
  7500.     if(!defined $$mp{"\0BOUNDARY"}){
  7501.  
  7502.         # create boundary on the fly
  7503.  
  7504.         my $b = uc(LW::utils_randstr(20));
  7505.  
  7506.         my $b2 = '-' x 32;
  7507.  
  7508.         $$mp{"\0BOUNDARY"}="$b2$b";
  7509.  
  7510.     }
  7511.  
  7512.  
  7513.  
  7514.     my $B = $$mp{"\0BOUNDARY"};
  7515.  
  7516.     my $EOL = $$hr{whisker}->{http_eol}||"\x0d\x0a";
  7517.  
  7518.  
  7519.  
  7520.     my $keycount=0;
  7521.  
  7522.     foreach (keys %$mp){
  7523.  
  7524.         next if(substr($_,0,1) eq "\0");
  7525.  
  7526.         $keycount++;
  7527.  
  7528.         if($$mp{$_} eq "\0FILE"){
  7529.  
  7530.             my ($path,$name)=LW::multipart_getfile($mp,$_);
  7531.  
  7532.             next if(!defined $path);
  7533.  
  7534.             $$hr{whisker}->{data}.="$B$EOL";
  7535.  
  7536.             $$hr{whisker}->{data}.="Content-Disposition: ".
  7537.  
  7538.                 "form-data; name=\"$_\"; ";
  7539.  
  7540.             $$hr{whisker}->{data}.="filename=\"$name\"$EOL";
  7541.  
  7542.             $$hr{whisker}->{data}.="Content-Type: ".
  7543.  
  7544.                 "application/octet-stream$EOL";
  7545.  
  7546.             $$hr{whisker}->{data}.=$EOL;
  7547.  
  7548.             next if(!open(IN,"<$path"));
  7549.  
  7550.             binmode(IN); # stupid Windows
  7551.  
  7552.             while(<IN>){
  7553.  
  7554.                 $$hr{whisker}->{data}.=$_; }
  7555.  
  7556.             close(IN);
  7557.  
  7558.             $$hr{whisker}->{data}.=$EOL;  # WARNING: is this right? 
  7559.  
  7560.         } else {
  7561.  
  7562.             $$hr{whisker}->{data}.="$B$EOL";
  7563.  
  7564.             $$hr{whisker}->{data}.="Content-Disposition: ".
  7565.  
  7566.                 "form-data; name=\"$_\"$EOL";
  7567.  
  7568.             $$hr{whisker}->{data}.="$EOL$$mp{$_}$EOL";
  7569.  
  7570.         }
  7571.  
  7572.     }
  7573.  
  7574.  
  7575.  
  7576.     if($keycount){
  7577.  
  7578.         $$hr{whisker}->{data}.="$B--$EOL"; # closing boundary
  7579.  
  7580.         $$hr{"Content-Length"}=length($$hr{whisker}->{data});
  7581.  
  7582.         $$hr{"Content-Type"}="multipart/form-data; boundary=$B";
  7583.  
  7584.         return 1;
  7585.  
  7586.     } else {
  7587.  
  7588.         # multipart hash didn't contain params to upload
  7589.  
  7590.         return undef;
  7591.  
  7592.     }
  7593.  
  7594. }
  7595.  
  7596.  
  7597.  
  7598. ########################################################################
  7599.  
  7600.  
  7601.  
  7602.  
  7603.  
  7604. =pod
  7605.  
  7606.  
  7607.  
  7608. =head1 - Function: LW::multipart_read
  7609.  
  7610.   
  7611.  
  7612. Params: \%multi_hash, \%hout_response [, $filepath ]
  7613.  
  7614. Return: 1 if successful, undef on error
  7615.  
  7616.  
  7617.  
  7618. LW::multipart_read will parse the data contents of the supplied
  7619.  
  7620. %hout_response hash, by passing the appropriate info to
  7621.  
  7622. multipart_read_data().  Please see multipart_read_data() for more
  7623.  
  7624. info on parameters and behaviour.
  7625.  
  7626.  
  7627.  
  7628. NOTE: this function will return an error if the given %hout_response
  7629.  
  7630. Content-Type is not set to "multipart/form-data".
  7631.  
  7632.  
  7633.  
  7634. =cut
  7635.  
  7636.  
  7637.  
  7638. sub multipart_read {
  7639.  
  7640.     my ($mp, $hr, $fp)=@_;
  7641.  
  7642.  
  7643.  
  7644.     return undef if(!(defined $mp && ref($mp)));
  7645.  
  7646.     return undef if(!(defined $hr && ref($hr)));
  7647.  
  7648.  
  7649.  
  7650.     my $ctype = LW::utils_find_lowercase_key($hr,'content-type');
  7651.  
  7652.     return undef if(!defined $ctype);
  7653.  
  7654.     return undef if($ctype!~m#^multipart/form-data#i);
  7655.  
  7656.  
  7657.  
  7658.     return LW::multipart_read_data($mp,
  7659.  
  7660.         \${$hr{'whisker'}->{'data'}},undef,$fp);
  7661.  
  7662.  
  7663.  
  7664. }
  7665.  
  7666.  
  7667.  
  7668. ########################################################################
  7669.  
  7670.  
  7671.  
  7672. =pod
  7673.  
  7674.  
  7675.  
  7676. =head1 - Function: LW::multipart_read_data
  7677.  
  7678.   
  7679.  
  7680. Params: \%multi_hash, \$data, $boundary [, $filepath ]
  7681.  
  7682. Return: 1 if successful, undef on error
  7683.  
  7684.  
  7685.  
  7686. LW::multipart_read_data parses the contents of the supplied data using 
  7687.  
  7688. the given boundary and puts the values in the supplied %multi_hash.  
  7689.  
  7690. Embedded files will *not* be saved unless a $filepath is given, which
  7691.  
  7692. should be a directory suitable for writing out temporary files.
  7693.  
  7694.  
  7695.  
  7696. NOTE: currently only application/octet-stream is the only supported
  7697.  
  7698. file encoding.  All other file encodings will not be parsed/saved.
  7699.  
  7700.  
  7701.  
  7702. =cut
  7703.  
  7704.  
  7705.  
  7706. sub multipart_read_data {
  7707.  
  7708.     my ($mp, $dr, $bound, $fp)=@_;
  7709.  
  7710.  
  7711.  
  7712.     return undef if(!(defined $mp && ref($mp)));
  7713.  
  7714.     return undef if(!(defined $dr && ref($dr)));
  7715.  
  7716.  
  7717.  
  7718.     # if $bound is undef, then we'll snag what looks to be
  7719.  
  7720.     # the first boundry from the data.
  7721.  
  7722.     if(!defined $bound){
  7723.  
  7724.         if($$dr=~/([-]{5,}[A-Z0-9]+)[\r\n]/i){
  7725.  
  7726.             $bound=$1;
  7727.  
  7728.         } else {
  7729.  
  7730.             # we didn't spot a typical boundary; error
  7731.  
  7732.             return undef;
  7733.  
  7734.         }
  7735.  
  7736.     }
  7737.  
  7738.  
  7739.  
  7740.     if(defined $fp && !(-d $fp && -w $fp)){
  7741.  
  7742.         $fp=undef; }
  7743.  
  7744.  
  7745.  
  7746.     my $line = LW::utils_getline_crlf($dr,0);
  7747.  
  7748.     return undef if(!defined $line);
  7749.  
  7750.     return undef if( index($line,$bound) != 0);
  7751.  
  7752.  
  7753.  
  7754.     my $done=0;
  7755.  
  7756.     while(!$done){
  7757.  
  7758.         $done=multipart_read_data_part($mp, $dr, $bound, $fp);
  7759.  
  7760.     }
  7761.  
  7762.  
  7763.  
  7764.     return 1;
  7765.  
  7766. }
  7767.  
  7768.  
  7769.  
  7770. ########################################################################
  7771.  
  7772.  
  7773.  
  7774. =pod
  7775.  
  7776.  
  7777.  
  7778. =head1 - Function: LW::multipart_read_data_part (INTERNAL)
  7779.  
  7780.   
  7781.  
  7782. Params: \%multi_hash, \$data, $boundary, $filepath
  7783.  
  7784. Return: 0 if more to read, 1 if done
  7785.  
  7786.  
  7787.  
  7788. This is an internal function used by multipart_read_data, and should
  7789.  
  7790. not be called on it's own.  This is the workhorse, and is quite nasty.
  7791.  
  7792.  
  7793.  
  7794. =cut
  7795.  
  7796.  
  7797.  
  7798. sub multipart_read_data_part {
  7799.  
  7800.     my ($mp, $dr, $bound, $fp)=@_;
  7801.  
  7802.  
  7803.  
  7804.     my $dispinfo = LW::utils_getline_crlf($dr);
  7805.  
  7806.     return 1 if(!defined $dispinfo);
  7807.  
  7808.     return 1 if(length($dispinfo)==0);
  7809.  
  7810.     my $lcdisp = lc($dispinfo);
  7811.  
  7812.  
  7813.  
  7814.     if(index($lcdisp,'content-disposition: form-data;') != 0){
  7815.  
  7816.         return 1; } # bad disposition
  7817.  
  7818.  
  7819.  
  7820.     my ($s,$e,$l);
  7821.  
  7822.  
  7823.  
  7824.     $s=index($lcdisp,'name="',30);
  7825.  
  7826.     $e=index($lcdisp, '"', $s+6);
  7827.  
  7828.     return 1 if($s == -1 || $e == -1);    
  7829.  
  7830.     my $NAME=substr($dispinfo,$s+6,$e-$s-6);
  7831.  
  7832.  
  7833.  
  7834.     $s=index($lcdisp,'filename="',$e);
  7835.  
  7836.     my $FILENAME=undef;
  7837.  
  7838.     if($s != -1){
  7839.  
  7840.         $e=index($lcdisp, '"', $s+10);
  7841.  
  7842.         return 1 if($e == -1); # puke; malformed filename
  7843.  
  7844.         $FILENAME=substr($dispinfo,$s+10,$e-$s-10);
  7845.  
  7846.         $s=rindex($FILENAME,'\\');
  7847.  
  7848.         $e=rindex($FILENAME,'/');
  7849.  
  7850.         $s=$e if($e>$s);
  7851.  
  7852.         $FILENAME=substr($FILENAME,$s+1,length($FILENAME)-$s);
  7853.  
  7854.     }
  7855.  
  7856.  
  7857.  
  7858.     my $CTYPE = LW::utils_getline_crlf($dr);
  7859.  
  7860.  
  7861.  
  7862.     return 1 if(!defined $CTYPE);
  7863.  
  7864.     $CTYPE = lc($CTYPE);
  7865.  
  7866.  
  7867.  
  7868.     if(length($CTYPE)>0){
  7869.  
  7870.         $s=index($CTYPE,'content-type:');
  7871.  
  7872.         return 1 if($s!=0); # bad ctype line
  7873.  
  7874.         $CTYPE=substr($CTYPE,13,length($CTYPE)-13);
  7875.  
  7876.         $CTYPE=~tr/ \t//d;
  7877.  
  7878.         my $xx=LW::utils_getline_crlf($dr);
  7879.  
  7880.         return 1 if(!defined $xx);
  7881.  
  7882.         return 1 if(length($xx)>0);
  7883.  
  7884.     } else {
  7885.  
  7886.         $CTYPE='application/octet-stream';
  7887.  
  7888.     }
  7889.  
  7890.  
  7891.  
  7892.  
  7893.  
  7894.     my $VALUE='';
  7895.  
  7896.     while( defined ($l=LW::utils_getline_crlf($dr)) ){
  7897.  
  7898.         last if(index($l,$bound)==0);    
  7899.  
  7900.         $VALUE.=$l;
  7901.  
  7902.         $VALUE.="\r\n";
  7903.  
  7904.     }
  7905.  
  7906.  
  7907.  
  7908.     substr($VALUE,-2,2)='';
  7909.  
  7910.  
  7911.  
  7912.     if(!defined $FILENAME){ # read in param
  7913.  
  7914.         $$mp{$NAME}=$VALUE;
  7915.  
  7916.         return 0;
  7917.  
  7918.  
  7919.  
  7920.     } else {  # read in file
  7921.  
  7922.         $$mp{$NAME}="\0FILE";
  7923.  
  7924.         return 0 if(!defined $fp);
  7925.  
  7926.  
  7927.  
  7928.         # TODO: funky content types, like application/x-macbinary
  7929.  
  7930.         if($CTYPE ne 'application/octet-stream'){
  7931.  
  7932.             return 0; }
  7933.  
  7934.  
  7935.  
  7936.         my $rfn = lc(LW::utils_randstr(12));
  7937.  
  7938.         my $fullpath = "$fp$rfn";
  7939.  
  7940.  
  7941.  
  7942.         $$mp{"\0$NAME"}=[undef,$FILENAME];
  7943.  
  7944.         return 0 if(!open(OUT,">$fullpath")); # error opening file
  7945.  
  7946.         binmode(OUT); # stupid Windows
  7947.  
  7948.         $$mp{"\0$NAME"}=[$fullpath,$FILENAME];
  7949.  
  7950.         print OUT $VALUE;
  7951.  
  7952.         close(OUT);
  7953.  
  7954.  
  7955.  
  7956.         return 0;
  7957.  
  7958.  
  7959.  
  7960.     } # if !defined $FILENAME
  7961.  
  7962.  
  7963.  
  7964.     return 0; # um, this should never be reached...
  7965.  
  7966. }
  7967.  
  7968.  
  7969.  
  7970.  
  7971.  
  7972. ########################################################################
  7973.  
  7974.  
  7975.  
  7976. =pod
  7977.  
  7978.  
  7979.  
  7980. =head1 - Function: LW::multipart_files_list
  7981.  
  7982.   
  7983.  
  7984. Params: \%multi_hash
  7985.  
  7986. Return: @files
  7987.  
  7988.  
  7989.  
  7990. LW::multipart_files_list returns an array of parameter names for all
  7991.  
  7992. the files that are contained in %multi_hash.
  7993.  
  7994.  
  7995.  
  7996. =cut
  7997.  
  7998.  
  7999.  
  8000. sub multipart_files_list {
  8001.  
  8002.     my ($mp)=shift;
  8003.  
  8004.     my @ret;
  8005.  
  8006.  
  8007.  
  8008.     return () if(!(defined $mp && ref($mp)));
  8009.  
  8010.     while( my ($K, $V)=each(%$mp)){
  8011.  
  8012.         push(@ret,$K) if($V eq "\0FILE"); }
  8013.  
  8014.     return @ret;
  8015.  
  8016. }
  8017.  
  8018.  
  8019.  
  8020.  
  8021.  
  8022. ########################################################################
  8023.  
  8024.  
  8025.  
  8026. =pod
  8027.  
  8028.  
  8029.  
  8030. =head1 - Function: LW::multipart_params_list
  8031.  
  8032.   
  8033.  
  8034. Params: \%multi_hash
  8035.  
  8036. Return: @params
  8037.  
  8038.  
  8039.  
  8040. LW::multipart_files_list returns an array of parameter names for all
  8041.  
  8042. the regular parameters (non-file) that are contained in %multi_hash.
  8043.  
  8044.  
  8045.  
  8046. =cut
  8047.  
  8048.  
  8049.  
  8050. sub multipart_params_list {
  8051.  
  8052.     my ($mp)=shift;
  8053.  
  8054.     my @ret;
  8055.  
  8056.  
  8057.  
  8058.     return () if(!(defined $mp && ref($mp)));
  8059.  
  8060.     while( my ($K, $V)=each(%$mp)){
  8061.  
  8062.         push(@ret,$K) if($V ne "\0FILE" &&
  8063.  
  8064.             substr($K,0,1) ne "\0" ); 
  8065.  
  8066.     }
  8067.  
  8068.     return @ret;
  8069.  
  8070. }
  8071.  
  8072.  
  8073.  
  8074. ########################################################################
  8075.  
  8076.  
  8077.  
  8078.  
  8079.  
  8080. =pod    
  8081.  
  8082.  
  8083.  
  8084.  
  8085.  
  8086. =head1 ++ Sub package: ntlm
  8087.  
  8088.         
  8089.  
  8090. The ntlm sub package implements ntlm authentication routines.
  8091.  
  8092.  
  8093.  
  8094. =cut
  8095.  
  8096.  
  8097.  
  8098. ########################################################################
  8099.  
  8100.  
  8101.  
  8102. =pod    
  8103.  
  8104.  
  8105.  
  8106. =head1 - Function: LW::ntlm_new
  8107.  
  8108.         
  8109.  
  8110. Params: $username, $password [, $domain, $ntlm_only]
  8111.  
  8112. Return: $ntlm_object
  8113.  
  8114.  
  8115.  
  8116. Returns a reference to an array (otherwise known as the 'ntlm object')
  8117.  
  8118. which contains the various informations specific to a user/pass combo.
  8119.  
  8120. If $ntlm_only is set to 1, then only the NTLM hash (and not the LanMan
  8121.  
  8122. hash) will be generated.  This results in a speed boost, and is typically
  8123.  
  8124. fine for using against IIS servers.
  8125.  
  8126.  
  8127.  
  8128. The array contains the following items, in order:
  8129.  
  8130. username, password, domain, lmhash(password), ntlmhash(password)
  8131.  
  8132.  
  8133.  
  8134. =cut
  8135.  
  8136.  
  8137.  
  8138. sub ntlm_new {
  8139.  
  8140.     my ($user,$pass,$domain,$flag)=@_; 
  8141.  
  8142.     $flag||=0;
  8143.  
  8144.     return undef if(!defined $user);
  8145.  
  8146.     $pass||=''; $domain||='';
  8147.  
  8148.     my @a=("$user","$pass","$domain",undef,undef);
  8149.  
  8150.     my $t;
  8151.  
  8152.  
  8153.  
  8154.     if($flag==0){
  8155.  
  8156.         $t=substr($pass,0,14);
  8157.  
  8158.         $t=~tr/a-z/A-Z/;
  8159.  
  8160.         $t.= "\0"x(14-length($t));
  8161.  
  8162.         $a[3]=des_E_P16($t); # LanMan password hash
  8163.  
  8164.         $a[3].= "\0"x(21-length($a[3]));
  8165.  
  8166.     }
  8167.  
  8168.  
  8169.  
  8170.     $t=md4(encode_unicode($pass));
  8171.  
  8172.     $t=~s/([a-z0-9]{2})/sprintf("%c",hex($1))/ieg;
  8173.  
  8174.     $t.="\0"x(21-length($t));
  8175.  
  8176.     $a[4]=$t; # NTLM password hash
  8177.  
  8178.  
  8179.  
  8180.     &des_cache_reset(); # reset the keys hash
  8181.  
  8182.     return \@a;
  8183.  
  8184. }
  8185.  
  8186.  
  8187.  
  8188. ########################################################################
  8189.  
  8190.  
  8191.  
  8192. =pod    
  8193.  
  8194.  
  8195.  
  8196. =head1 - Function: LW::ntlm_generate_responses (INTERNAL)
  8197.  
  8198.         
  8199.  
  8200. Params: $ntlm_object, $challenge_token
  8201.  
  8202. Return: $lanman_hash, $ntlm_hash
  8203.  
  8204.  
  8205.  
  8206. Returns the challenge responses to the given tokens, using the password
  8207.  
  8208. set in the $ntlm_object.
  8209.  
  8210.  
  8211.  
  8212. =cut
  8213.  
  8214.  
  8215.  
  8216. sub ntlm_generate_responses {
  8217.  
  8218.     my ($obj,$chal)=@_;
  8219.  
  8220.     return (undef,undef) if(!defined $obj || !defined $chal);
  8221.  
  8222.     return (undef,undef) if(!ref($obj));
  8223.  
  8224.     my $x='';
  8225.  
  8226.     $x=des_E_P24($obj->[3], $chal) if(defined $obj->[3]);
  8227.  
  8228.     return ($x, des_E_P24($obj->[4], $chal));
  8229.  
  8230. }
  8231.  
  8232.  
  8233.  
  8234. ########################################################################
  8235.  
  8236.  
  8237.  
  8238. =pod    
  8239.  
  8240.  
  8241.  
  8242. =head1 - Function: LW::ntlm_decode_challenge (INTERNAL)
  8243.  
  8244.         
  8245.  
  8246. Params: $challenge
  8247.  
  8248. Return: @challenge_parts
  8249.  
  8250.  
  8251.  
  8252. Splits the supplied challenge into the various parts.  The returned array
  8253.  
  8254. contains elements in the following order:
  8255.  
  8256.  
  8257.  
  8258. unicode_domain, ident, packet_type, domain_len, domain_maxlen,
  8259.  
  8260. domain_offset, flags, challenge_token, reserved, empty, raw_data
  8261.  
  8262.  
  8263.  
  8264. =cut
  8265.  
  8266.  
  8267.  
  8268. sub ntlm_decode_challenge {
  8269.  
  8270.   return undef if(!defined $_[0]);
  8271.  
  8272.   my $chal=shift;
  8273.  
  8274.   my @res;
  8275.  
  8276.  
  8277.  
  8278.   @res=unpack('Z8VvvVVa8a8a8',substr($chal,0,48));
  8279.  
  8280.   push(@res,substr($chal,48));
  8281.  
  8282.   unshift(@res,substr($chal,$res[4],$res[2]));
  8283.  
  8284.   return @res;
  8285.  
  8286. }
  8287.  
  8288.  
  8289.  
  8290. ########################################################################
  8291.  
  8292.  
  8293.  
  8294. =pod    
  8295.  
  8296.  
  8297.  
  8298. =head1 - Function: LW::ntlm_header (INTERNAL)
  8299.  
  8300.         
  8301.  
  8302. Params: $string, $header_length, $offset
  8303.  
  8304. Return: $header
  8305.  
  8306.  
  8307.  
  8308. Constructs an appropriate header for the supplied $string.
  8309.  
  8310.  
  8311.  
  8312. =cut
  8313.  
  8314.  
  8315.  
  8316. sub ntlm_header {
  8317.  
  8318.     my ($s,$h,$o)=@_;
  8319.  
  8320.     my $l=length($s);
  8321.  
  8322.     return pack('vvV',0,0,$o-$h) if($l==0);
  8323.  
  8324.     return pack('vvV',$l,$l,$o);
  8325.  
  8326. }
  8327.  
  8328.  
  8329.  
  8330. ########################################################################
  8331.  
  8332.  
  8333.  
  8334. =pod    
  8335.  
  8336.  
  8337.  
  8338. =head1 - Function: LW::ntlm_client
  8339.  
  8340.         
  8341.  
  8342. Params: $ntlm_obj [, $server_challenge]
  8343.  
  8344. Return: $response
  8345.  
  8346.  
  8347.  
  8348. ntlm_client() is responsible for generating the base64-encoded text you
  8349.  
  8350. include in the HTTP Authorization header.  If you call ntlm_client()
  8351.  
  8352. without a $server_challenge, the function will return the initial NTLM
  8353.  
  8354. request packet (message packet #1).  You send this to the server, and
  8355.  
  8356. take the server's response (message packet #2) and pass that as
  8357.  
  8358. $server_challenge, causing ntlm_client() to generate the final response
  8359.  
  8360. packet (message packet #3).
  8361.  
  8362.  
  8363.  
  8364. Note: $server_challenge is expected to be base64 encoded.
  8365.  
  8366.  
  8367.  
  8368. =cut
  8369.  
  8370.  
  8371.  
  8372. sub ntlm_client {
  8373.  
  8374.     my ($obj,$p)=@_;
  8375.  
  8376.     my $resp="NTLMSSP\0";
  8377.  
  8378.  
  8379.  
  8380.     return undef if(!defined $obj || !ref($obj));
  8381.  
  8382.  
  8383.  
  8384.     if(defined $p && $p ne ''){ # answer challenge
  8385.  
  8386.         $p=~tr/ \t\r\n//d;
  8387.  
  8388.         $p=LW::decode_base64($p);
  8389.  
  8390.         my @c=ntlm_decode_challenge($p);
  8391.  
  8392.         $uu=encode_unicode($obj->[0]); # username
  8393.  
  8394.         $resp.=pack('V',3);
  8395.  
  8396.         my($hl,$hn)=ntlm_generate_responses($obj,$c[7]); # token
  8397.  
  8398.         return undef if(!defined $hl || !defined $hn);
  8399.  
  8400.         my $o=64;
  8401.  
  8402.         $resp.=ntlm_header($hl,64,$o);            # LM hash
  8403.  
  8404.         $resp.=ntlm_header($hn,64,($o+=length($hl)));    # NTLM hash
  8405.  
  8406.         $resp.=ntlm_header($c[0],64,($o+=length($hn)));    # domain
  8407.  
  8408.         $resp.=ntlm_header($uu,64,($o+=length($c[0])));    # username
  8409.  
  8410.         $resp.=ntlm_header($uu,64,($o+=length($uu)));     # workstation
  8411.  
  8412.         $resp.=ntlm_header('',64,($o+=length($uu)));    # session
  8413.  
  8414.         $resp.=pack('V',$c[6]);
  8415.  
  8416.         $resp.=$hl.$hn.$c[0].$uu.$uu;
  8417.  
  8418.  
  8419.  
  8420.     } else { # initiate challenge
  8421.  
  8422.         $resp.=pack('VV',1,0x0000b207);
  8423.  
  8424.         $resp.=ntlm_header($obj->[0],32,32);
  8425.  
  8426.         $resp.=ntlm_header($obj->[2],32,32+length($obj->[0]));
  8427.  
  8428.         $resp .= $obj->[0].$obj->[2];
  8429.  
  8430.     }
  8431.  
  8432.  
  8433.  
  8434.     return encode_base64($resp,'');
  8435.  
  8436. }
  8437.  
  8438.  
  8439.  
  8440. ########################################################################
  8441.  
  8442.  
  8443.  
  8444. =pod    
  8445.  
  8446.  
  8447.  
  8448.  
  8449.  
  8450. =head1 ++ Sub package: ntlm_des
  8451.  
  8452.         
  8453.  
  8454. The ntlm_des sub package implements unchained forward DES in perl, which
  8455.  
  8456. is needed by the ntlm auth package to do it's thing.  Note that
  8457.  
  8458. unchained forward DES is not a symmetrical cipher--it's much more like
  8459.  
  8460. using DES as a digest/hash algorithm.  Thus there is very little
  8461.  
  8462. practical reuse of this code outside of NTLM authentication.
  8463.  
  8464.  
  8465.  
  8466. The code below has also been 'tweaked' for the reuse of the set of keys,
  8467.  
  8468. which is typical when requiring multiple authentication runs.  This leads
  8469.  
  8470. to a speed increase when multiple authentications are needed.
  8471.  
  8472.  
  8473.  
  8474. The code below is a highly-modified version of Authen::NTLM::DES.pm,
  8475.  
  8476. written by Mark.Bush@bushnet.demon.co.uk.  Portions of the code below
  8477.  
  8478. bear the following copyrights:
  8479.  
  8480.  
  8481.  
  8482. Copyright (C) 2001 Mark Bush. <Mark.Bush@bushnet.demon.co.uk>
  8483.  
  8484.  
  8485.  
  8486. The code is based on fetchmail code which is Copyright (C) 1997 Eric
  8487.  
  8488. S. Raymond.
  8489.  
  8490.  
  8491.  
  8492. Fetchmail uses SMB/Netbios code from samba which is Copyright (C)
  8493.  
  8494. Andrew Tridgell 1992-1998 with modifications from Jeremy Allison.
  8495.  
  8496.  
  8497.  
  8498. All the des_* functions should be considered internal and not called
  8499.  
  8500. directly.
  8501.  
  8502.  
  8503.  
  8504. =cut
  8505.  
  8506.  
  8507.  
  8508. { # start of DES local container #######################################
  8509.  
  8510. my $generated=0;
  8511.  
  8512. my $perm1 = [57, 49, 41, 33, 25, 17, 9,    1, 58, 50, 42, 34, 26, 18,
  8513.  
  8514.          10, 2, 59, 51, 43, 35, 27,    19, 11, 3, 60, 52, 44, 36,
  8515.  
  8516.          63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22,
  8517.  
  8518.          14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4];
  8519.  
  8520. my $perm2 = [14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10,
  8521.  
  8522.          23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2,
  8523.  
  8524.          41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
  8525.  
  8526.          44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32];
  8527.  
  8528. my $perm3 = [58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
  8529.  
  8530.          62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
  8531.  
  8532.          57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
  8533.  
  8534.          61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7];
  8535.  
  8536. my $perm4 = [32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
  8537.  
  8538.          8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
  8539.  
  8540.          16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
  8541.  
  8542.          24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1];
  8543.  
  8544. my $perm5 = [16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
  8545.  
  8546.          2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25];
  8547.  
  8548. my $perm6 = [40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
  8549.  
  8550.          38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
  8551.  
  8552.          36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
  8553.  
  8554.          34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41,  9, 49, 17, 57, 25];
  8555.  
  8556. my $sc = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1];
  8557.  
  8558.  
  8559.  
  8560. sub des_E_P16 {
  8561.  
  8562.   my ($p14) = @_;
  8563.  
  8564.   my $sp8 = [0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25];
  8565.  
  8566.   my $p7 = substr($p14, 0, 7);
  8567.  
  8568.   my $p16 = des_smbhash($sp8, $p7);
  8569.  
  8570.   $p7 = substr($p14, 7, 7);
  8571.  
  8572.   $p16 .= des_smbhash($sp8, $p7);
  8573.  
  8574.   return $p16;
  8575.  
  8576. }
  8577.  
  8578.  
  8579.  
  8580. sub des_E_P24 {
  8581.  
  8582.   my ($p21, $c8_str) = @_;
  8583.  
  8584.   my @c8 = map {ord($_)} split(//, $c8_str);
  8585.  
  8586.   my $p24 = des_smbhash(\@c8, substr($p21, 0, 7));
  8587.  
  8588.   $p24 .= des_smbhash(\@c8, substr($p21, 7, 7));
  8589.  
  8590.   $p24 .= des_smbhash(\@c8, substr($p21, 14, 7));
  8591.  
  8592. }
  8593.  
  8594.  
  8595.  
  8596. sub des_permute {
  8597.  
  8598.   my ($i,$out, $in, $p, $n) = (0,@_);
  8599.  
  8600.   foreach $i (0..($n-1)){
  8601.  
  8602.     $out->[$i] = $in->[$p->[$i]-1]; }
  8603.  
  8604. }
  8605.  
  8606.  
  8607.  
  8608. sub des_lshift {
  8609.  
  8610.     my ($c, $d, $count)=@_;
  8611.  
  8612.     my (@outc, @outd, $i, $x);
  8613.  
  8614.     while($count--){
  8615.  
  8616.         push @$c, shift @$c;
  8617.  
  8618.         push @$d, shift @$d;
  8619.  
  8620.     }
  8621.  
  8622. }
  8623.  
  8624.  
  8625.  
  8626. my %dohash_cache; # cache for key data; saves some cycles
  8627.  
  8628. my %key_cache;      # another cache for key data
  8629.  
  8630. sub des_cache_reset {
  8631.  
  8632.     %dohash_cache=();
  8633.  
  8634.     %key_cache=();
  8635.  
  8636. }
  8637.  
  8638.  
  8639.  
  8640. sub des_dohash
  8641.  
  8642. {
  8643.  
  8644.   my ($out, $in, $key) = @_;
  8645.  
  8646.   my ($i, $j, $k, @pk1, @c, @d, @cd,
  8647.  
  8648.       @ki, @pd1, @l, @r, @rl);
  8649.  
  8650.  
  8651.  
  8652. # if(!defined $dohash_cache{$skey}){
  8653.  
  8654.   &des_permute(\@pk1, $key, $perm1, 56);
  8655.  
  8656.  
  8657.  
  8658.   for($i=0;$i<28;$i++) {
  8659.  
  8660.     $c[$i] = $pk1[$i];
  8661.  
  8662.     $d[$i] = $pk1[$i+28];
  8663.  
  8664.   }
  8665.  
  8666.   for($i=0;$i<16;$i++){
  8667.  
  8668.     my @array;
  8669.  
  8670.     &des_lshift(\@c,\@d,$sc->[$i]);
  8671.  
  8672.     @cd = (@c, @d);
  8673.  
  8674.     &des_permute(\@array, \@cd, $perm2, 48);
  8675.  
  8676.     $ki[$i] = \@array;
  8677.  
  8678. #    $dohash_cache{$skey}->[$i]=\@array; 
  8679.  
  8680.   }
  8681.  
  8682. # } else {
  8683.  
  8684. #    for($i=0;$i<16;$i++){
  8685.  
  8686. #        $ki[$i]=$dohash_cache{$skey}->[$i];}
  8687.  
  8688. # }
  8689.  
  8690.  
  8691.  
  8692.   des_dohash2($in,\@l,\@r,\@ki);
  8693.  
  8694.  
  8695.  
  8696.   @rl = (@r, @l);
  8697.  
  8698.   &des_permute($out, \@rl, $perm6, 64);
  8699.  
  8700. }
  8701.  
  8702.  
  8703.  
  8704. sub des_str_to_key{
  8705.  
  8706.   my ($str) = @_;
  8707.  
  8708.   my ($i,@key,$out);
  8709.  
  8710.   unshift(@str,ord($_))while($_=chop($str));
  8711.  
  8712.   $key[0] = $str[0]>>1;
  8713.  
  8714.   $key[1] = (($str[0]&0x01)<<6) | ($str[1]>>2);
  8715.  
  8716.   $key[2] = (($str[1]&0x03)<<5) | ($str[2]>>3);
  8717.  
  8718.   $key[3] = (($str[2]&0x07)<<4) | ($str[3]>>4);
  8719.  
  8720.   $key[4] = (($str[3]&0x0f)<<3) | ($str[4]>>5);
  8721.  
  8722.   $key[5] = (($str[4]&0x1f)<<2) | ($str[5]>>6);
  8723.  
  8724.   $key[6] = (($str[5]&0x3f)<<1) | ($str[6]>>7);
  8725.  
  8726.   $key[7] = $str[6]&0x7f;
  8727.  
  8728.   foreach $i (0..7) {
  8729.  
  8730.     $key[$i] = 0xff&($key[$i]<<1); }
  8731.  
  8732.   @{$key_cache{$str}}=@key;
  8733.  
  8734.   return \@key;
  8735.  
  8736. }
  8737.  
  8738.  
  8739.  
  8740. sub des_smbhash
  8741.  
  8742. {
  8743.  
  8744.   # use faster binary helper
  8745.  
  8746.   goto &LW::bin::des_smbhash if(defined $LW::available{'lw::bin'});
  8747.  
  8748.  
  8749.  
  8750.   my ($in, $key) = @_;
  8751.  
  8752.   my $key2;
  8753.  
  8754.  
  8755.  
  8756.   &des_generate if(!$generated);
  8757.  
  8758.   if(defined $key_cache{$key}){    $key2=$key_cache{$key};
  8759.  
  8760.   } else {            $key2=&des_str_to_key($key); }
  8761.  
  8762.  
  8763.  
  8764.  my ($i, $div, $mod, @in, @outb, @inb, @keyb, @out);
  8765.  
  8766.   foreach $i (0..63){
  8767.  
  8768.     $div = int($i/8); $mod = $i%8;
  8769.  
  8770.     $inb[$i] = ($in->[$div] & (1<<(7-($mod))))? 1: 0;
  8771.  
  8772.     $keyb[$i] = ($key2->[$div] & (1<<(7-($mod))))? 1: 0;
  8773.  
  8774.     $outb[$i] = 0;
  8775.  
  8776.   }
  8777.  
  8778.   &des_dohash(\@outb, \@inb, \@keyb);
  8779.  
  8780.   foreach $i (0..7){ $out[$i] = 0; }
  8781.  
  8782.   foreach $i (0..63){
  8783.  
  8784.     $out[int($i/8)] |= (1<<(7-($i%8))) if ($outb[$i]); }
  8785.  
  8786.   my $out = pack("C8", @out);
  8787.  
  8788.  
  8789.  
  8790.   return $out;
  8791.  
  8792. }
  8793.  
  8794.  
  8795.  
  8796.  
  8797.  
  8798. sub des_generate { # really scary dragons here....this code is optimized
  8799.  
  8800.            # for speed, and not readability
  8801.  
  8802.  my ($i,$j);
  8803.  
  8804.  my $code=<<EOT;
  8805.  
  8806. { my \$sbox = [[
  8807.  
  8808. [14,4,13,1,2,15,11,8,3,10,6,12,5,9,0,7],[0,15,7,4,14,2,13,1,10,6,12,11,9,5,3,8],
  8809.  
  8810. [4,1,14,8,13,6,2,11,15,12,9,7,3,10,5,0],[15,12,8,2,4,9,1,7,5,11,3,14,10,0,6,13]
  8811.  
  8812. ],[
  8813.  
  8814. [15,1,8,14,6,11,3,4,9,7,2,13,12,0,5,10],[3,13,4,7,15,2,8,14,12,0,1,10,6,9,11,5],
  8815.  
  8816. [0,14,7,11,10,4,13,1,5,8,12,6,9,3,2,15],[13,8,10,1,3,15,4,2,11,6,7,12,0,5,14,9]
  8817.  
  8818. ],[
  8819.  
  8820. [10,0,9,14,6,3,15,5,1,13,12,7,11,4,2,8],[13,7,0,9,3,4,6,10,2,8,5,14,12,11,15,1],
  8821.  
  8822. [13,6,4,9,8,15,3,0,11,1,2,12,5,10,14,7],[1,10,13,0,6,9,8,7,4,15,14,3,11,5,2,12]
  8823.  
  8824. ],[
  8825.  
  8826. [7,13,14,3,0,6,9,10,1,2,8,5,11,12,4,15],[13,8,11,5,6,15,0,3,4,7,2,12,1,10,14,9],
  8827.  
  8828. [10,6,9,0,12,11,7,13,15,1,3,14,5,2,8,4],[3,15,0,6,10,1,13,8,9,4,5,11,12,7,2,14]
  8829.  
  8830. ],[
  8831.  
  8832. [2,12,4,1,7,10,11,6,8,5,3,15,13,0,14,9],[14,11,2,12,4,7,13,1,5,0,15,10,3,9,8,6],
  8833.  
  8834. [4,2,1,11,10,13,7,8,15,9,12,5,6,3,0,14],[11,8,12,7,1,14,2,13,6,15,0,9,10,4,5,3]
  8835.  
  8836. ],[
  8837.  
  8838. [12,1,10,15,9,2,6,8,0,13,3,4,14,7,5,11],[10,15,4,2,7,12,9,5,6,1,13,14,0,11,3,8],
  8839.  
  8840. [9,14,15,5,2,8,12,3,7,0,4,10,1,13,11,6],[4,3,2,12,9,5,15,10,11,14,1,7,6,0,8,13]
  8841.  
  8842. ],[
  8843.  
  8844. [4,11,2,14,15,0,8,13,3,12,9,7,5,10,6,1],[13,0,11,7,4,9,1,10,14,3,5,12,2,15,8,6],
  8845.  
  8846. [1,4,11,13,12,3,7,14,10,15,6,8,0,5,9,2],[6,11,13,8,1,4,10,7,9,5,0,15,14,2,3,12]
  8847.  
  8848. ],[
  8849.  
  8850. [13,2,8,4,6,15,11,1,10,9,3,14,5,0,12,7],[1,15,13,8,10,3,7,4,12,5,6,11,0,14,9,2],
  8851.  
  8852. [7,11,4,1,9,12,14,2,0,6,10,13,15,3,5,8],[2,1,14,7,4,10,8,13,15,12,9,0,3,5,6,11]
  8853.  
  8854. ]];
  8855.  
  8856. EOT
  8857.  
  8858.  
  8859.  
  8860.  $code.='sub des_dohash2 { my ($in,$l,$r,$ki)=@_; my (@p,$i,$j,$k,$m,$n);';
  8861.  
  8862.  for($i=0;$i<64;$i++){
  8863.  
  8864.     $code.="\$p[$i] = \$in->[".($perm3->[$i]-1)."];\n"; }
  8865.  
  8866.  for($i=0;$i<32;$i++){
  8867.  
  8868.     $code.="\$l->[$i]=\$p[$i]; \$r->[$i]=\$p[".($i+32)."];\n"; }
  8869.  
  8870.  $code.='for($i=0;$i<16;$i++){ local (@er,@erk,@b,@cb,@pcb,@r2);';
  8871.  
  8872.  for($i=0;$i<48;$i++){
  8873.  
  8874.     $code.="\$erk[$i]=\$r->[".($perm4->[$i]-1)."]^(\$ki->[\$i]->[$i]);\n"; }
  8875.  
  8876.  for($i=0;$i<8;$i++){
  8877.  
  8878.     for($j=0;$j<6;$j++){
  8879.  
  8880.         $code.="\$b[$i][$j]=\$erk[".($i*6+$j)."];\n"; }}
  8881.  
  8882.  for($i=0;$i<8;$i++){
  8883.  
  8884.     $code.="\$m=(\$b[$i][0]<<1)|\$b[$i][5];";
  8885.  
  8886.     $code.="\$n=(\$b[$i][1]<<3)|(\$b[$i][2]<<2)|(\$b[$i][3]<<1)|\$b[$i][4];";
  8887.  
  8888.     for($j=0;$j<4;$j++){
  8889.  
  8890.         $code.="\$b[$i][$j]=(\$sbox->[$i][\$m][\$n]&".(1<<(3-$j)).")?1:0;"; }}
  8891.  
  8892.  for($i=0;$i<8;$i++){
  8893.  
  8894.     for($j=0;$j<4;$j++){
  8895.  
  8896.         $code.="\$cb[".($i*4+$j)."]=\$b[$i][$j];\n"; }}
  8897.  
  8898.  for($i=0;$i<32;$i++){
  8899.  
  8900.     $code.="\$pcb[$i]=\$cb[".($perm5->[$i]-1)."];\n"; }
  8901.  
  8902.  for($i=0;$i<32;$i++){
  8903.  
  8904.     $code.="\$r2[$i]=(\$l->[$i])^\$pcb[$i];\n"; }
  8905.  
  8906.  for($i=0;$i<32;$i++){
  8907.  
  8908.     $code.="\$l->[$i]=\$r->[$i]; \$r->[$i]=\$r2[$i];\n"; }
  8909.  
  8910.  $code.='}}}';
  8911.  
  8912.  
  8913.  
  8914.  eval "$code";
  8915.  
  8916.  $generated++;
  8917.  
  8918. }
  8919.  
  8920.  
  8921.  
  8922. } ##### end of DES container ################################################
  8923.  
  8924.  
  8925.  
  8926.  
  8927.  
  8928. =pod
  8929.  
  8930.  
  8931.  
  8932. =head1 ++ Sub package: utils
  8933.  
  8934.  
  8935.  
  8936. The utils subpackage contains various utility functions which serve
  8937.  
  8938. different purposes.
  8939.  
  8940.  
  8941.  
  8942. =cut
  8943.  
  8944.  
  8945.  
  8946. ########################################################################
  8947.  
  8948.  
  8949.  
  8950. =pod
  8951.  
  8952.  
  8953.  
  8954. =head1 - Function: LW::utils_recperm
  8955.  
  8956.   
  8957.  
  8958. Params: $uri, $depth, \@dir_parts, \@valid, \&func, \%track, \%arrays, \&cfunc
  8959.  
  8960. Return: nothing
  8961.  
  8962.  
  8963.  
  8964. This is a special function which is used to recursively-permutate through
  8965.  
  8966. a given directory listing.  This is really only used by whisker, in order
  8967.  
  8968. to traverse down directories, testing them as it goes.  See whisker 2.0 for
  8969.  
  8970. exact usage examples.
  8971.  
  8972.  
  8973.  
  8974. =cut
  8975.  
  8976.  
  8977.  
  8978. # '/', 0, \@dir.split, \@valid, \&func, \%track, \%arrays, \&cfunc
  8979.  
  8980. sub utils_recperm {
  8981.  
  8982.  my ($p, $pp, $pn, $r, $fr, $dr, $ar, $cr)=(shift,shift,@_);
  8983.  
  8984.  $p=~s#/+#/#g; if($pp >= @$pn) { push @$r, $p if &$cr($$dr{$p});
  8985.  
  8986.  } else { my $c=$$pn[$pp];
  8987.  
  8988.   if($c!~/^\@/){ utils_recperm($p.$c.'/',$pp+1,@_) if(&$fr($p.$c.'/'));
  8989.  
  8990.   } else {    $c=~tr/\@//d; if(defined $$ar{$c}){
  8991.  
  8992.         foreach $d (@{$$ar{$c}}){
  8993.  
  8994.             if(&$fr($p.$d.'/')){
  8995.  
  8996.                   utils_recperm($p.$d.'/',$pp+1,@_);}}}}}}
  8997.  
  8998.  
  8999.  
  9000.  
  9001.  
  9002. #################################################################
  9003.  
  9004.  
  9005.  
  9006. =pod
  9007.  
  9008.  
  9009.  
  9010. =head1 - Function: LW::utils_array_shuffle
  9011.  
  9012.   
  9013.  
  9014. Params: @array
  9015.  
  9016. Return: nothing
  9017.  
  9018.  
  9019.  
  9020. This function will randomize the order of the elements in the given array.
  9021.  
  9022.  
  9023.  
  9024. =cut
  9025.  
  9026.  
  9027.  
  9028. sub utils_array_shuffle { # fisher yates shuffle....w00p!
  9029.  
  9030.         my $array=shift; my $i;
  9031.  
  9032.         for ($i = @$array; --$i;){
  9033.  
  9034.                 my $j = int rand ($i+1);
  9035.  
  9036.                 next if $i==$j;
  9037.  
  9038.                 @$array[$i,$j]=@$array[$j,$i];
  9039.  
  9040. }} # end array_shuffle, from Perl Cookbook (rock!)
  9041.  
  9042.  
  9043.  
  9044.  
  9045.  
  9046. #################################################################
  9047.  
  9048.  
  9049.  
  9050. =pod
  9051.  
  9052.  
  9053.  
  9054. =head1 - Function: LW::utils_randstr
  9055.  
  9056.   
  9057.  
  9058. Params: [ $size, $chars ]
  9059.  
  9060. Return: $random_string
  9061.  
  9062.  
  9063.  
  9064. This function generates a random string between 10 and 20 characters
  9065.  
  9066. long, or of $size if specified.  If $chars is specified, then the
  9067.  
  9068. random function picks characters from the supplied string.  For example,
  9069.  
  9070. to have a random string of 10 characters, composed of only the characters
  9071.  
  9072. 'abcdef', then you would run:
  9073.  
  9074.  
  9075.  
  9076. LW::utils_randstr(10,'abcdef');
  9077.  
  9078.  
  9079.  
  9080. The default character string is alphanumeric.
  9081.  
  9082.  
  9083.  
  9084. =cut
  9085.  
  9086.  
  9087.  
  9088. sub utils_randstr {
  9089.  
  9090.         my $str;
  9091.  
  9092.         my $drift=shift||((rand() * 10) % 10)+10; 
  9093.  
  9094.  
  9095.  
  9096.     # 'a'..'z' doesn't seem to work on string assignment :(
  9097.  
  9098.     my $CHARS = shift || 'abcdefghijklmnopqrstuvwxyz' .
  9099.  
  9100.             'ABCDEFGHIJKLMNOPQRSTUVWXYZ' .
  9101.  
  9102.             '0123456789';
  9103.  
  9104.  
  9105.  
  9106.     my $L = length($CHARS);
  9107.  
  9108.         for(1..$drift){
  9109.  
  9110.             $str .= substr($CHARS,((rand() * $L) % $L),1);
  9111.  
  9112.     }
  9113.  
  9114.         return $str;}
  9115.  
  9116.  
  9117.  
  9118. #################################################################
  9119.  
  9120.  
  9121.  
  9122. =pod
  9123.  
  9124.  
  9125.  
  9126. =head1 - Function: LW::utils_get_dir
  9127.  
  9128.   
  9129.  
  9130. Params: $uri
  9131.  
  9132. Return: $uri_directory
  9133.  
  9134.  
  9135.  
  9136. Will take a URI and return the directory base of it, i.e. /rfp/page.php 
  9137.  
  9138. will return /rfp/.
  9139.  
  9140.  
  9141.  
  9142. =cut
  9143.  
  9144.  
  9145.  
  9146. sub utils_get_dir {
  9147.  
  9148.         my ($w,$URL)=(0,shift);
  9149.  
  9150.  
  9151.  
  9152.     return undef if(!defined $URL);
  9153.  
  9154.  
  9155.  
  9156.     $URL=substr($URL,0,$w) if( ($w=index($URL,'#')) >= 0);
  9157.  
  9158.     $URL=substr($URL,0,$w) if( ($w=index($URL,'?')) >= 0);
  9159.  
  9160.  
  9161.  
  9162.     if( ($w=rindex($URL,'/')) >= 0){
  9163.  
  9164.         $URL = substr($URL,0,$w+1);
  9165.  
  9166.     }
  9167.  
  9168.         return $URL; 
  9169.  
  9170. }
  9171.  
  9172.  
  9173.  
  9174.  
  9175.  
  9176. #################################################################
  9177.  
  9178.  
  9179.  
  9180. =pod
  9181.  
  9182.  
  9183.  
  9184. =head1 - Function: LW::utils_port_open
  9185.  
  9186.   
  9187.  
  9188. Params: $host, $port
  9189.  
  9190. Return: $result
  9191.  
  9192.  
  9193.  
  9194. Quick function to attempt to make a connection to the given host and
  9195.  
  9196. port.  If a connection was successfully made, function will return true
  9197.  
  9198. (1).  Otherwise it returns false (0).
  9199.  
  9200.  
  9201.  
  9202. Note: this uses standard TCP connections, thus is not recommended for use
  9203.  
  9204. in port-scanning type applications.  Extremely slow.
  9205.  
  9206.  
  9207.  
  9208. =cut
  9209.  
  9210.  
  9211.  
  9212. sub utils_port_open {  # this should be platform-safe
  9213.  
  9214.         my ($target,$port)=@_;
  9215.  
  9216.  
  9217.  
  9218.     return 0 if(!defined $target || !defined $port);
  9219.  
  9220.  
  9221.  
  9222.         if(!(socket(S,PF_INET,SOCK_STREAM,0))){ return 0;}
  9223.  
  9224.         if(connect(S,sockaddr_in($port,inet_aton($target)))){
  9225.  
  9226.                 close(S); return 1;
  9227.  
  9228.         } else { return 0;}}
  9229.  
  9230.  
  9231.  
  9232.  
  9233.  
  9234. #################################################################
  9235.  
  9236.  
  9237.  
  9238. =pod
  9239.  
  9240.  
  9241.  
  9242. =head1 - Function: LW::utils_split_uri
  9243.  
  9244.   
  9245.  
  9246. Params: $uri_string [, \%hin_request]
  9247.  
  9248. Return: @uri_parts
  9249.  
  9250.  
  9251.  
  9252. Return an array of the following values, in order:  uri, protocol, host,
  9253.  
  9254. port, params, frag, user, password.  Values not defined are given an undef
  9255.  
  9256. value.  If a %hin_request hash is passed in, then utils_split_uri() will
  9257.  
  9258. also set the appropriate values in the hash.  While it attempts to do
  9259.  
  9260. RFC-compliant URI parsing, it still caters to HTTP[S] only.
  9261.  
  9262.  
  9263.  
  9264. Note:  utils_split_uri() will only set the %hin_request if the protocol
  9265.  
  9266. is HTTP or HTTPS!
  9267.  
  9268.  
  9269.  
  9270. =cut
  9271.  
  9272.  
  9273.  
  9274. sub utils_split_uri {
  9275.  
  9276.     my ($uri,$w)=(shift,'',0);
  9277.  
  9278.     my ($hr)=shift;
  9279.  
  9280.     my @res=(undef,'http',undef,0,undef,undef,undef,undef);
  9281.  
  9282.  
  9283.  
  9284.     return undef if(!defined $uri);
  9285.  
  9286.  
  9287.  
  9288.     # remove fragments
  9289.  
  9290.     ($uri,$res[5])=split('#',$uri,2) if(index($uri,'#',0) >=0);
  9291.  
  9292.  
  9293.  
  9294.     # get scheme and net_loc
  9295.  
  9296.     my $net_loc = undef;
  9297.  
  9298.     if($uri=~s/^([-+.a-z0-9A-Z]+)://){
  9299.  
  9300.         $res[1]=lc($1);
  9301.  
  9302.         if(substr($uri,0,2) eq '//'){
  9303.  
  9304.             $w=index($uri,'/',2);
  9305.  
  9306.             if($w >= 0){
  9307.  
  9308.                 $net_loc=substr($uri,2,$w-2);
  9309.  
  9310.                 $uri=substr($uri,$w,length($uri)-$w);
  9311.  
  9312.             } else {
  9313.  
  9314.                 ($net_loc=$uri)=~tr#/##d;
  9315.  
  9316.                 $uri='/';
  9317.  
  9318.             }
  9319.  
  9320.         }
  9321.  
  9322.     }
  9323.  
  9324.  
  9325.  
  9326.  
  9327.  
  9328.     # parse net_loc info
  9329.  
  9330.     if(defined $net_loc){
  9331.  
  9332.         if(index($net_loc,'@',0) >=0){
  9333.  
  9334.             ($res[6],$net_loc)=split('@',$net_loc,2);
  9335.  
  9336.             if(index($res[6],':',0) >=0){
  9337.  
  9338.                 ($res[6],$res[7])=split(':',$res[6],2);
  9339.  
  9340.             }
  9341.  
  9342.         }
  9343.  
  9344.         $res[3]=$1 if($net_loc=~s/:([0-9]+)$//);
  9345.  
  9346.         $res[2]=$net_loc;
  9347.  
  9348.     }
  9349.  
  9350.  
  9351.  
  9352.     # remove query info
  9353.  
  9354.     ($uri,$res[4])=split('\?',$uri,2) if(index($uri,'?',0) >=0);
  9355.  
  9356.  
  9357.  
  9358.     # whatever is left over is the uri
  9359.  
  9360.     $res[0]=$uri;
  9361.  
  9362.  
  9363.  
  9364.     if($res[3]==0 && defined $res[1]){
  9365.  
  9366.         $res[3]=80 if($res[1] eq 'http');
  9367.  
  9368.         $res[3]=443 if($res[1] eq 'https');
  9369.  
  9370.     }
  9371.  
  9372.  
  9373.  
  9374.     return @res if($res[3]==0);
  9375.  
  9376.  
  9377.  
  9378.     # setup whisker hash
  9379.  
  9380.     if(defined $hr && ref($hr)){
  9381.  
  9382.         # these must always exist
  9383.  
  9384.         $$hr{whisker}->{uri}=$res[0]         if(defined $res[0]);
  9385.  
  9386.         $$hr{whisker}->{ssl}=1            if($res[1] eq 'https');
  9387.  
  9388.         $$hr{whisker}->{host}=$res[2]        if(defined $res[2]);
  9389.  
  9390.         $$hr{whisker}->{port}=$res[3]        ;
  9391.  
  9392.  
  9393.  
  9394.         # set/delete parameter attributes
  9395.  
  9396.         if(defined $res[4]){
  9397.  
  9398.             $$hr{whisker}->{uri_param}=$res[4];
  9399.  
  9400.         } else { delete $$hr{whisker}->{uri_param}; }
  9401.  
  9402.         if(defined $res[6]){
  9403.  
  9404.             $$hr{whisker}->{uri_user}=$res[6];
  9405.  
  9406.         } else { delete $$hr{whisker}->{uri_user}; }
  9407.  
  9408.         if(defined $res[7]){
  9409.  
  9410.             $$hr{whisker}->{uri_password}=$res[7];
  9411.  
  9412.         } else { delete $$hr{whisker}->{uri_password}; }
  9413.  
  9414.     }
  9415.  
  9416.         
  9417.  
  9418.     return @res;
  9419.  
  9420. }
  9421.  
  9422.  
  9423.  
  9424. #################################################################
  9425.  
  9426. =pod
  9427.  
  9428.  
  9429.  
  9430. =head1 - Function: LW::utils_lowercase_headers
  9431.  
  9432.   
  9433.  
  9434. Params: \%hash
  9435.  
  9436. Return: nothing
  9437.  
  9438.  
  9439.  
  9440. Will lowercase all the header names (but not values) of the given hash.
  9441.  
  9442.  
  9443.  
  9444. =cut
  9445.  
  9446.  
  9447.  
  9448. sub utils_lowercase_headers {
  9449.  
  9450.     goto &utils_lowercase_hashkeys;
  9451.  
  9452. }
  9453.  
  9454.  
  9455.  
  9456. #################################################################
  9457.  
  9458. =pod
  9459.  
  9460.  
  9461.  
  9462. =head1 - Function: LW::utils_lowercase_hashkeys
  9463.  
  9464.   
  9465.  
  9466. Params: \%hash
  9467.  
  9468. Return: nothing
  9469.  
  9470.  
  9471.  
  9472. Will lowercase all the header names (but not values) of the given hash.
  9473.  
  9474.  
  9475.  
  9476. =cut
  9477.  
  9478.  
  9479.  
  9480. sub utils_lowercase_hashkeys {
  9481.  
  9482.     my $href=shift;
  9483.  
  9484.  
  9485.  
  9486.     return if(!(defined $href && ref($href)));
  9487.  
  9488.  
  9489.  
  9490.     while( my ($key,$val)=each %$href ){
  9491.  
  9492.         delete $$href{$key};
  9493.  
  9494.         $$href{lc($key)}=$val;
  9495.  
  9496.     }
  9497.  
  9498. }
  9499.  
  9500.  
  9501.  
  9502. #################################################################
  9503.  
  9504. =pod
  9505.  
  9506.  
  9507.  
  9508. =head1 - Function: LW::utils_find_lowercase_key
  9509.  
  9510.   
  9511.  
  9512. Params: \%hash, $key
  9513.  
  9514. Return: $value, undef on error or not exist
  9515.  
  9516.  
  9517.  
  9518. Searches the given hash for the $key (regardless of case), and
  9519.  
  9520. returns the value.
  9521.  
  9522.  
  9523.  
  9524. =cut
  9525.  
  9526.  
  9527.  
  9528. sub utils_find_lowercase_key {
  9529.  
  9530.     my ($href,$key)=(shift,lc(shift));
  9531.  
  9532.  
  9533.  
  9534.     return undef if(!(defined $href && ref($href)));
  9535.  
  9536.     return undef if(!defined $key);    
  9537.  
  9538.  
  9539.  
  9540.     while( my ($k,$v)=each %$href ){
  9541.  
  9542.         return $v if(lc($k) eq $key);
  9543.  
  9544.     }
  9545.  
  9546.     return undef;
  9547.  
  9548. }
  9549.  
  9550.  
  9551.  
  9552. #################################################################
  9553.  
  9554.  
  9555.  
  9556. =pod
  9557.  
  9558.  
  9559.  
  9560. =head1 - Function: LW::utils_join_uri
  9561.  
  9562.   
  9563.  
  9564. Params: @vals
  9565.  
  9566. Return: $url
  9567.  
  9568.  
  9569.  
  9570. Takes the @vals array output from utils_split_uri, and returns a single 
  9571.  
  9572. scalar/string with them joined again, in the form of:
  9573.  
  9574. protocol://user:password@host:port/uri?params#frag
  9575.  
  9576.  
  9577.  
  9578. =cut
  9579.  
  9580.  
  9581.  
  9582. sub utils_join_uri {
  9583.  
  9584.     my @V=@_;
  9585.  
  9586.     my $URL;
  9587.  
  9588.  
  9589.  
  9590.     $URL.=$V[1].':' if defined $V[1];
  9591.  
  9592.     if(defined $V[2]){
  9593.  
  9594.         $URL.='//';
  9595.  
  9596.         if(defined $V[6]){
  9597.  
  9598.             $URL.=$V[6];
  9599.  
  9600.             $URL.=':'.$V[7] if defined $V[7];
  9601.  
  9602.             $URL.='@';
  9603.  
  9604.         }
  9605.  
  9606.         $URL.=$V[2];
  9607.  
  9608.     }
  9609.  
  9610.     if($V[3]>0){
  9611.  
  9612.         my $no = 0;
  9613.  
  9614.         $no++ if($V[3]==80 && defined $V[1] && $V[1] eq 'http');
  9615.  
  9616.         $no++ if($V[3]==443 && defined $V[1] && $V[1] eq 'https');
  9617.  
  9618.         $URL .= ':'.$V[3] if(!$no);
  9619.  
  9620.     }
  9621.  
  9622.     $URL.=$V[0];
  9623.  
  9624.     $URL .= '?'.$V[4] if defined $V[4];
  9625.  
  9626.     $URL .= '#'.$V[5] if defined $V[5];
  9627.  
  9628.     return $URL;
  9629.  
  9630. }
  9631.  
  9632.  
  9633.  
  9634. #################################################################
  9635.  
  9636.  
  9637.  
  9638. =pod
  9639.  
  9640.  
  9641.  
  9642. =head1 - Function: LW::utils_getline
  9643.  
  9644.   
  9645.  
  9646. Params: \$data [, $resetpos ]
  9647.  
  9648. Return: $line (undef if no more data)
  9649.  
  9650.  
  9651.  
  9652. Fetches the next \n terminated line from the given data.  Use
  9653.  
  9654. the optional $resetpos to reset the internal position pointer.
  9655.  
  9656. Does *NOT* return trialing \n.
  9657.  
  9658.  
  9659.  
  9660. =cut
  9661.  
  9662.  
  9663.  
  9664. { $POS=0;
  9665.  
  9666. sub utils_getline {
  9667.  
  9668.     my ($dr, $rp)=@_;
  9669.  
  9670.  
  9671.  
  9672.     return undef if(!(defined $dr && ref($dr)));
  9673.  
  9674.     $POS=$rp if(defined $rp);
  9675.  
  9676.  
  9677.  
  9678.     my $where=index($$dr,"\n",$POS);
  9679.  
  9680.     return undef if($where==-1);
  9681.  
  9682.  
  9683.  
  9684.     my $str=substr($$dr,$POS,$where-$POS);
  9685.  
  9686.     $POS=$where+1;
  9687.  
  9688.  
  9689.  
  9690.     return $str;
  9691.  
  9692. }}
  9693.  
  9694.  
  9695.  
  9696. #################################################################
  9697.  
  9698.  
  9699.  
  9700. =pod
  9701.  
  9702.  
  9703.  
  9704. =head1 - Function: LW::utils_getline_crlf
  9705.  
  9706.   
  9707.  
  9708. Params: \$data [, $resetpos ]
  9709.  
  9710. Return: $line (undef if no more data)
  9711.  
  9712.  
  9713.  
  9714. Fetches the next \r\n terminated line from the given data.  Use
  9715.  
  9716. the optional $resetpos to reset the internal position pointer.
  9717.  
  9718. Does *NOT* return trialing \r\n.
  9719.  
  9720.  
  9721.  
  9722. =cut
  9723.  
  9724.  
  9725.  
  9726. { $POS=0;
  9727.  
  9728. sub utils_getline_crlf {
  9729.  
  9730.     my ($dr, $rp)=@_;
  9731.  
  9732.  
  9733.  
  9734.     return undef if(!(defined $dr && ref($dr)));
  9735.  
  9736.     $POS=$rp if(defined $rp);
  9737.  
  9738.  
  9739.  
  9740.     my $tpos=$POS;
  9741.  
  9742.     while(1){
  9743.  
  9744.         my $where=index($$dr,"\n",$tpos);
  9745.  
  9746.         return undef if($where==-1);
  9747.  
  9748.  
  9749.  
  9750.         if(substr($$dr,$where-1,1) eq "\r"){
  9751.  
  9752.             my $str=substr($$dr,$POS,$where-$POS-1);
  9753.  
  9754.             $POS=$where+1;
  9755.  
  9756.             return $str;
  9757.  
  9758.         } else {
  9759.  
  9760.             $tpos=$where+1;
  9761.  
  9762.         }
  9763.  
  9764.     }
  9765.  
  9766. }}
  9767.  
  9768.  
  9769.  
  9770. #################################################################
  9771.  
  9772.  
  9773.  
  9774. =pod
  9775.  
  9776.  
  9777.  
  9778. =head1 - Function: LW::utils_absolute_uri
  9779.  
  9780.   
  9781.  
  9782. Params: $uri, $base_uri [, $normalize_flag ]
  9783.  
  9784. Return: $absolute_$url
  9785.  
  9786.  
  9787.  
  9788. Double checks that the given $uri is in absolute form (that is,
  9789.  
  9790. "http://host/file"), and if not (it's in the form "/file"), then
  9791.  
  9792. it will append the given $base_uri to make it absolute.  This
  9793.  
  9794. provides a compatibility similar to that found in the URI
  9795.  
  9796. subpackage.
  9797.  
  9798.  
  9799.  
  9800. If $normalize_flag is set to 1, then the output will be passed
  9801.  
  9802. through utils_normalize_uri before being returned.
  9803.  
  9804.  
  9805.  
  9806. =cut
  9807.  
  9808.  
  9809.  
  9810. sub utils_absolute_uri {
  9811.  
  9812.         my ($uri, $buri, $norm)=@_;
  9813.  
  9814.         return undef if(!defined $uri || !defined $buri);
  9815.  
  9816.     return $uri if($uri=~m#^[a-zA-Z]+://#);
  9817.  
  9818.  
  9819.  
  9820.     if(substr($uri,0,1) eq '/'){
  9821.  
  9822.         if($buri=~m#^[a-zA-Z]+://#){
  9823.  
  9824.             my @p=utils_split_uri($buri);
  9825.  
  9826.             $buri="$p[1]://$p[2]";
  9827.  
  9828.             $buri.=":$p[3]" if($p[3]!=80);
  9829.  
  9830.             $buri.='/';
  9831.  
  9832.         } else { # ah suck, base URI isn't absolute...
  9833.  
  9834.             return $uri;
  9835.  
  9836.         }
  9837.  
  9838.     } else {
  9839.  
  9840.         $buri=~s/[?#].*$//; # remove params and frags
  9841.  
  9842.         $buri.='/' if($buri=~m#^[a-z]+://[^/]+$#i);
  9843.  
  9844.         $buri=~s#/[^/]*$#/#;
  9845.  
  9846.     }
  9847.  
  9848.     return utils_normalize_uri("$buri$uri") 
  9849.  
  9850.         if(defined $norm && $norm > 0);
  9851.  
  9852.         return $buri.$uri;
  9853.  
  9854. }
  9855.  
  9856.  
  9857.  
  9858. #################################################################
  9859.  
  9860.  
  9861.  
  9862. =pod
  9863.  
  9864.  
  9865.  
  9866. =head1 - Function: LW::utils_normalize_uri
  9867.  
  9868.   
  9869.  
  9870. Params: $uri [, $fix_windows_slashes ]
  9871.  
  9872. Return: $normalized_uri
  9873.  
  9874.  
  9875.  
  9876. Takes the given $uri and does any /./ and /../ dereferencing in
  9877.  
  9878. order to come up with the correct absolute URL.  If the $fix_
  9879.  
  9880. windows_slashes parameter is set to 1, all \ (back slashes) will
  9881.  
  9882. be converted to / (forward slashes).
  9883.  
  9884.  
  9885.  
  9886. =cut
  9887.  
  9888.  
  9889.  
  9890. sub utils_normalize_uri {
  9891.  
  9892.     my ($host,$uri, $win)=('',@_);
  9893.  
  9894.  
  9895.  
  9896.     $uri=~tr#\\#/# if(defined $win && $win>0);
  9897.  
  9898.  
  9899.  
  9900.     if($uri=~s#^([-+.a-z0-9A-Z]+:)##){
  9901.  
  9902.         return undef if($1 ne 'http:' && $1 ne 'https:');
  9903.  
  9904.         $host=$1;
  9905.  
  9906.         return undef unless($uri=~s#^(//[^/]+)##);
  9907.  
  9908.         $host.=$1; }
  9909.  
  9910.     return "$host/" if($uri eq '' || $uri eq '/');
  9911.  
  9912.  
  9913.  
  9914.     # fast path check
  9915.  
  9916.     return "$host$uri" if(index($uri,'/.')<0);
  9917.  
  9918.  
  9919.  
  9920.     # parse order/steps as defined in RFC 1808
  9921.  
  9922.     1 while($uri=~s#/\./#/# || $uri=~s#//#/#);
  9923.  
  9924.     $uri=~s#/\.$#/#;
  9925.  
  9926.     1 while($uri=~s#[^/]+/\.\./##);
  9927.  
  9928.     1 while($uri=~s#^/\.\./#/#);
  9929.  
  9930.     $uri=~s#[^/]*/\.\.$##;
  9931.  
  9932.     $uri||='/';
  9933.  
  9934.     return $host.$uri;
  9935.  
  9936. }
  9937.  
  9938.  
  9939.  
  9940. #################################################################
  9941.  
  9942.  
  9943.  
  9944. =pod
  9945.  
  9946.  
  9947.  
  9948. =head1 - Function: LW::utils_save_page
  9949.  
  9950.   
  9951.  
  9952. Params: $file, \%response
  9953.  
  9954. Return: 0 on success, 1 on error
  9955.  
  9956.  
  9957.  
  9958. Saves the data portion of the given whisker %response hash to the
  9959.  
  9960. indicated file.  Can technically save the data portion of a
  9961.  
  9962. %request hash too.  A file is not written if there is no data.
  9963.  
  9964.  
  9965.  
  9966. Note: LW does not do any special file checking; files are opened
  9967.  
  9968. in overwrite mode.
  9969.  
  9970.  
  9971.  
  9972. =cut
  9973.  
  9974.  
  9975.  
  9976. sub utils_save_page {
  9977.  
  9978.     my ($file, $hr)=@_;
  9979.  
  9980.     return 1 if(!ref($hr) || ref($file));
  9981.  
  9982.     return 0 if(!defined $$hr{'whisker'} || 
  9983.  
  9984.         !defined $$hr{'whisker'}->{'data'});
  9985.  
  9986.     open(OUT,">$file") || return 1;
  9987.  
  9988.     print OUT $$hr{'whisker'}->{'data'};
  9989.  
  9990.     close(OUT);
  9991.  
  9992.     return 0;
  9993.  
  9994. }
  9995.  
  9996.  
  9997.  
  9998. #################################################################
  9999.  
  10000.  
  10001.  
  10002. =pod
  10003.  
  10004.  
  10005.  
  10006. =head1 - Function: LW::utils_getopts
  10007.  
  10008.   
  10009.  
  10010. Params: $opt_str, \%opt_results
  10011.  
  10012. Return: 0 on success, 1 on error
  10013.  
  10014.  
  10015.  
  10016. This function is a general implementation of GetOpts::Std.  It will
  10017.  
  10018. parse @ARGV, looking for the options specified in $opt_str, and will
  10019.  
  10020. put the results in %opt_results.  Behavior/parameter values are
  10021.  
  10022. similar to GetOpts::Std's getopts().
  10023.  
  10024.  
  10025.  
  10026. Note: this function does *not* support long options (--option),
  10027.  
  10028. option grouping (-opq), or options with immediate values (-ovalue).
  10029.  
  10030. If an option is indicated as having a value, it will take the next
  10031.  
  10032. argument regardless.
  10033.  
  10034.  
  10035.  
  10036. =cut
  10037.  
  10038.  
  10039.  
  10040. sub utils_getopts {
  10041.  
  10042.         my ($str,$ref)=@_;
  10043.  
  10044.         my (%O,$l);
  10045.  
  10046.         my @left;
  10047.  
  10048.  
  10049.  
  10050.         return 1 if($str=~tr/-:a-zA-Z0-9//c);
  10051.  
  10052.  
  10053.  
  10054.         while($str=~m/([a-z0-9]:{0,1})/ig){
  10055.  
  10056.                 $l=$1;
  10057.  
  10058.                 if($l=~tr/://d){        $O{$l}=1;
  10059.  
  10060.                 } else {                $O{$l}=0; }
  10061.  
  10062.         }
  10063.  
  10064.  
  10065.  
  10066.         while($l=shift(@ARGV)){
  10067.  
  10068.                 push(@left,$l)&&next if(substr($l,0,1) ne '-');
  10069.  
  10070.                 push(@left,$l)&&next if($l eq '-');
  10071.  
  10072.                 substr($l,0,1)='';
  10073.  
  10074.                 if(length($l)!=1){
  10075.  
  10076.                         %$ref=();
  10077.  
  10078.                         return 1; }
  10079.  
  10080.                 if($O{$l}==1){
  10081.  
  10082.                         my $x=shift(@ARGV);
  10083.  
  10084.                         $$ref{$l}=$x;
  10085.  
  10086.                 } else { $$ref{$l}=1; }
  10087.  
  10088.         }
  10089.  
  10090.  
  10091.  
  10092.         @ARGV=@left;
  10093.  
  10094.         return 0;
  10095.  
  10096. }
  10097.  
  10098.  
  10099.  
  10100. #################################################################
  10101.  
  10102.  
  10103.  
  10104. =pod
  10105.  
  10106.  
  10107.  
  10108. =head1 - Function: LW::utils_unidecode_uri
  10109.  
  10110.   
  10111.  
  10112. Params: $unicode_string
  10113.  
  10114. Return: $decoded_string
  10115.  
  10116.  
  10117.  
  10118. This function attempts to decode a unicode (UTF-8) string by
  10119.  
  10120. converting it into a single-byte-character string.  Overlong 
  10121.  
  10122. characters are converted to their standard characters in place; 
  10123.  
  10124. non-overlong (aka multi-byte) characters are substituted with the 
  10125.  
  10126. 0xff; invalid encoding characters are left as-is.
  10127.  
  10128.  
  10129.  
  10130. Note: this function is useful for dealing with the various unicode
  10131.  
  10132. exploits/vulnerabilities found in web servers; it is *not* good for
  10133.  
  10134. doing actual UTF-8 parsing, since characters over a single byte are
  10135.  
  10136. basically dropped/replaced with a placeholder.
  10137.  
  10138.  
  10139.  
  10140. =cut
  10141.  
  10142.  
  10143.  
  10144. sub utils_unidecode_uri {
  10145.  
  10146.         my $str = $_[0];
  10147.  
  10148.         return $str if($str!~tr/!-~//c); # fastpath
  10149.  
  10150.         my ($lead,$count,$idx);
  10151.  
  10152.         my $out='';
  10153.  
  10154.         my $len = length($str);
  10155.  
  10156.         my ($ptr,$no,$nu)=(0,0,0);
  10157.  
  10158.  
  10159.  
  10160.         while($ptr < $len){
  10161.  
  10162.                 my $c=substr($str,$ptr,1);
  10163.  
  10164.                 if( ord($c) >= 0xc0 && ord($c) <= 0xfd){
  10165.  
  10166.                         $count=0;
  10167.  
  10168.                         $c=ord($c)<<1;
  10169.  
  10170.                         while( ($c & 0x80) == 0x80){
  10171.  
  10172.                                 $c<<=1;
  10173.  
  10174.                                 last if($count++ ==4);
  10175.  
  10176.                         }
  10177.  
  10178.                         $c = ($c & 0xff);
  10179.  
  10180.                         for( $idx=1; $idx<$count; $idx++){
  10181.  
  10182.                                 my $o=ord(substr($str,$ptr+$idx,1));
  10183.  
  10184.                                 $no=1 if($o != 0x80);
  10185.  
  10186.                                 $nu=1 if($o <0x80 || $o >0xbf);
  10187.  
  10188.                         }
  10189.  
  10190.                         my $o=ord(substr($str,$ptr+$idx,1));
  10191.  
  10192.                         $nu=1 if( $o < 0x80 || $o > 0xbf);
  10193.  
  10194.                         if($nu){
  10195.  
  10196.                                 $out.=substr($str,$ptr++,1);
  10197.  
  10198.                         } else {
  10199.  
  10200.                                 if($no){
  10201.  
  10202.                                         $out.="\xff"; # generic replacement char
  10203.  
  10204.                                 } else {
  10205.  
  10206.                                         my $prior=ord(substr($str,$ptr+$count-1,1))<<6;
  10207.  
  10208.                                         $out.= pack("C", (ord(substr($str,$ptr+$count,1) )&0x7f)+$prior);
  10209.  
  10210.                                 }
  10211.  
  10212.                                 $ptr += $count+1;
  10213.  
  10214.                         }
  10215.  
  10216.                         $no=$nu=0;
  10217.  
  10218.                 } else {
  10219.  
  10220.                         $out.=$c;
  10221.  
  10222.                         $ptr++;
  10223.  
  10224.                 }
  10225.  
  10226.         }
  10227.  
  10228.         return $out;
  10229.  
  10230. }
  10231.  
  10232.  
  10233.  
  10234. #################################################################
  10235.  
  10236.  
  10237.  
  10238. =pod
  10239.  
  10240.  
  10241.  
  10242. =head1 - Function: LW::utils_text_wrapper
  10243.  
  10244.   
  10245.  
  10246. Params: $long_text_string [, $crlf, $width ]
  10247.  
  10248. Return: $formatted_test_string
  10249.  
  10250.  
  10251.  
  10252. This is a simple function used to format a long line of text for
  10253.  
  10254. display on a typical limited-character screen, such as a unix
  10255.  
  10256. shell console.
  10257.  
  10258.  
  10259.  
  10260. $crlf defaults to "\n", and $width defaults to 76.
  10261.  
  10262.  
  10263.  
  10264. =cut
  10265.  
  10266.  
  10267.  
  10268. sub utils_text_wrapper {
  10269.  
  10270.         my ($out,$w,$str,$crlf,$width)=('',0,@_);
  10271.  
  10272.     $crlf||="\n";    $width||=76;
  10273.  
  10274.         $str.=$crlf if($str!~/$crlf$/);
  10275.  
  10276.         return $str if(length($str)<=$width);
  10277.  
  10278.         while(length($str)>$width){
  10279.  
  10280.                 my $w1=rindex($str,' ',$width);
  10281.  
  10282.                 my $w2=rindex($str,"\t",$width);
  10283.  
  10284.                 if($w1>$w2){ $w=$w1; } else { $w=$w2; }
  10285.  
  10286.                 if($w==-1){    $w=$width;
  10287.  
  10288.             } else {    substr($str,$w,1)=''; }
  10289.  
  10290.                 $out.=substr($str,0,$w,'');
  10291.  
  10292.                 $out.=$crlf;
  10293.  
  10294.         }
  10295.  
  10296.         return $out.$str;
  10297.  
  10298. }
  10299.  
  10300.  
  10301.  
  10302. #################################################################
  10303.  
  10304.  
  10305.  
  10306. 1;
  10307.